Does { } act like ( ) when creating a new object in C#?
I just noticed that using {}
instead of ()
gives the same results when constructing an object.
class Customer
{
public string name;
public string ID {get; set;}
}
static void Main()
{
Customer c1= new Customer{}; //Is this a constructor?
Customer c2= new Customer();
//what is the concept behind the ability to assign values for properties
//and fields inside the {} and is not allowable to do it inside ()
//without defining a constructor:
Customer c3= new Customer{name= "John", ID="ABC"};
}
Does {}
act like ()
when creating a new object in C#?
Solution 1:
There are three ways of directly creating a new object in C#:
-
A simple constructor call with an argument list:
new Foo() // Empty argument list new Foo(10, 20) // Passing arguments
-
An object initializer with an argument list
new Foo() { Name = "x" } // Empty argument list new Foo(10, 20) { Name = "x" } // Two arguments
-
An object initializer with no argument list
new Foo { Name = "x" }
The last form is exactly equivalent to specifying an empty argument list. Usually it will call a parameterless constructor, but it could call a constructor where all the parameters have default values.
Now in both the object initializer examples I've given, I've set a Name
property - and you could set other properties/fields, or even set no properties and fields. So all three of these are equivalent, effectively passing no constructor arguments and specifying no properties/fields to set:
new Foo()
new Foo() {}
new Foo {}
Of these, the first is the most conventional.
Solution 2:
()
- calls the parameterless constructor.
{}
- is supposed to be used to assign properties.
Using {}
without ()
is a shortcut and will work as long as there is a parameterless constructor.
Solution 3:
You can use object initializers to initialize type objects in a declarative manner without explicitly invoking a constructor for the type.
https://msdn.microsoft.com/en-us/library/bb397680.aspx
Also you can omit calling the constructor if the type has default contructor. So
Customer c1 = new Customer { };
is exactly the same as
Customer c1 = new Customer() { };
Solution 4:
Customer c1 = new Customer {};
This is an empty object initializer. As per the spec, object initializers will call the default constructor unless you specify the constructor to use. Since no initialization is done, it will be compiled the same as using the default constructor.
To answer the question, if ‘{}
act[s] like ()
when creating a new object in C#’, we have to go into more detail. For all you care about, the resulting objects will contain the same data, but the generated IL is not identical.
The following example
namespace SO28254462
{
class Program
{
class Customer
{
private readonly Foo foo = new Foo();
public string name;
public Customer()
{
}
public Customer(string id)
{
this.ID = id;
}
public string ID { get; set; }
public Foo Foo
{
get
{
return this.foo;
}
}
}
class Foo
{
public string Bar { get; set; }
}
static void Main(string[] args)
{
Customer c1 = new Customer { };
Customer c2 = new Customer();
Customer c3 = new Customer { name = "John", ID = "ABC", Foo = { Bar = "whatever" } };
Customer c4 = new Customer();
c4.name = "John";
c4.ID = "ABC";
c4.Foo.Bar = "whatever";
Customer c5 = new Customer("ABC") { name = "John", Foo = { Bar = "whatever" } };
Customer c6 = new Customer("ABC");
c6.name = "John";
c6.Foo.Bar = "whatever";
}
}
}
will compile to this IL (main method only, Debug without optimizations)
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 201 (0xc9)
.maxstack 2
.entrypoint
.locals init (
[0] class SO28254462.Program/Customer c1,
[1] class SO28254462.Program/Customer c2,
[2] class SO28254462.Program/Customer c3,
[3] class SO28254462.Program/Customer c4,
[4] class SO28254462.Program/Customer c5,
[5] class SO28254462.Program/Customer c6,
[6] class SO28254462.Program/Customer '<>g__initLocal0',
[7] class SO28254462.Program/Customer '<>g__initLocal1'
)
IL_0000: nop
IL_0001: newobj instance void SO28254462.Program/Customer::.ctor()
IL_0006: stloc.0
IL_0007: newobj instance void SO28254462.Program/Customer::.ctor()
IL_000c: stloc.1
IL_000d: newobj instance void SO28254462.Program/Customer::.ctor()
IL_0012: stloc.s '<>g__initLocal0'
IL_0014: ldloc.s '<>g__initLocal0'
IL_0016: ldstr "John"
IL_001b: stfld string SO28254462.Program/Customer::name
IL_0020: ldloc.s '<>g__initLocal0'
IL_0022: ldstr "ABC"
IL_0027: callvirt instance void SO28254462.Program/Customer::set_ID(string)
IL_002c: nop
IL_002d: ldloc.s '<>g__initLocal0'
IL_002f: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
IL_0034: ldstr "whatever"
IL_0039: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
IL_003e: nop
IL_003f: ldloc.s '<>g__initLocal0'
IL_0041: stloc.2
IL_0042: newobj instance void SO28254462.Program/Customer::.ctor()
IL_0047: stloc.3
IL_0048: ldloc.3
IL_0049: ldstr "John"
IL_004e: stfld string SO28254462.Program/Customer::name
IL_0053: ldloc.3
IL_0054: ldstr "ABC"
IL_0059: callvirt instance void SO28254462.Program/Customer::set_ID(string)
IL_005e: nop
IL_005f: ldloc.3
IL_0060: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
IL_0065: ldstr "whatever"
IL_006a: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
IL_006f: nop
IL_0070: ldstr "ABC"
IL_0075: newobj instance void SO28254462.Program/Customer::.ctor(string)
IL_007a: stloc.s '<>g__initLocal1'
IL_007c: ldloc.s '<>g__initLocal1'
IL_007e: ldstr "John"
IL_0083: stfld string SO28254462.Program/Customer::name
IL_0088: ldloc.s '<>g__initLocal1'
IL_008a: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
IL_008f: ldstr "whatever"
IL_0094: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
IL_0099: nop
IL_009a: ldloc.s '<>g__initLocal1'
IL_009c: stloc.s c5
IL_009e: ldstr "ABC"
IL_00a3: newobj instance void SO28254462.Program/Customer::.ctor(string)
IL_00a8: stloc.s c6
IL_00aa: ldloc.s c6
IL_00ac: ldstr "John"
IL_00b1: stfld string SO28254462.Program/Customer::name
IL_00b6: ldloc.s c6
IL_00b8: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
IL_00bd: ldstr "whatever"
IL_00c2: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
IL_00c7: nop
IL_00c8: ret
} // end of method Program::Main
As we can see, the first two Customer
initializations have been converted into identical IL (IL_0001 to IL_000c). After that, it gets more interesting: From IL_000d to IL_0041, we see that a new object is created and assigned to the invisible temporary variable <>g__initLocal0
and only after that is done, the resulting value is assigned to c3
. This is how the object initializer is implemented by the C# compiler. The difference to setting the public properties "manually" after instantiating the object are obvious when you look at how c4
is initialized from IL_0042 to IL_006a - there is no temporary variable.
IL_0070 till the end are equivalent to the previous examples, except they use a non-default constructor in combination with object initializers. As you may expect, it simply gets compiled the same way as before, but with the specified constructor.
Long story short: The outcome, from a C# developer's point of view, is basically the same. Object initializers are simple syntactic sugar and compiler tricks which can help to make code easier to read. However FWIW, the resulting IL is not identical to the manual initialization of the properties. Still, the cost of the invisible temporary variable that's emitted by the compiler should be insignificant in almost all C# programs.
Solution 5:
No new version of C# implicitly create a Constructor for object initialization
Customer c1= new Customer{};
Above is same as
Customer c1= new Customer()
{
};
Object and Collection Initializers (C# Programming Guide)