62

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#?

  • 1
    you *can* pass values in the ctor [`()`] if you provide an overload to accept and use them. the curly braces are for iniitializing – Ňɏssa Pøngjǣrdenlarp Jan 31 '15 at 17:34
  • Not an answer because you already got those, just a comment: `{}` does provide a way to assing values, basically nothing more than a shorthand to instantiate the object first then assign the values one by one. However, the constructor is in no way limited to this. It's completely up to you what arguments you pass to it and whether to use those arguments to assign default values, make computations, call other parts of your code, as you please. – Gábor Jan 31 '15 at 20:11

7 Answers7

107

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 10
    @NadiaChibrikova: It sounds like you're using `new {}` rather than `new Test{}`. There shouldn't be any anonymous types involved. – Jon Skeet Jan 31 '15 at 18:32
  • Can you also just say `new Foo;` with *neither* parenthesis nor braces? If not, why not? – Harry Johnston Feb 01 '15 at 22:52
  • From a C++ background, creating an object with no parentheses means that no constructor is called (for POD types), ie the object defaults to whatever garbage is on the stack. That's probably why it wasn't included in C# -- it goes against everything C# stands for to have garbage initialized objects. – Blindy Feb 02 '15 at 03:33
  • 1
    @Blindy: Well, that and the fact that in C# the value of a reference type variable is a reference, not an object. For value type local variables, the compiler ensures all fields are definitely assigned before the variable is read; for value type fields, they just get the "0 value" for all fields in the struct. – Jon Skeet Feb 02 '15 at 06:46
  • @HarryJohnston: No, you can't. – Jon Skeet Feb 02 '15 at 06:47
48

() - 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.

Nadia Chibrikova
  • 4,916
  • 1
  • 15
  • 17
5

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() { };
Nikolay Kostov
  • 16,433
  • 23
  • 85
  • 123
3
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.

hangy
  • 10,765
  • 6
  • 43
  • 63
2

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)

MMM
  • 3,132
  • 3
  • 20
  • 32
1

Customer c1= new Customer{} - this is initializer of properties. You can write it as:

Customer c1 = new Customer{
              name="some text",
              ID="some id"
              };
vladb9582
  • 17
  • 4
1

In your specific scenario, yes, it would produce a same result, but not for a reason you probably think.

To understand the result, let's say you have a class like this:

class Customer
{
    public string name;
    public string ID {get; set;}

    public Customer() 
    { 
    }

    public Customer(string n, string id)
    {
        name = n;
        ID = id;
    }
}

When you create it like this:

Customer c = new Customer("john", "someID");

You call the second constructor and tell the class you're passing these values and the responsibility to do what it thinks it's the best lies on the constructor (in this case it would use these values to pass them to public fields).

When you create it like this:

Customer c = new Customer { name = "john", ID = "someID" };

You're setting the public fields by yourself and you use the empty constructor.

Either way, you should AVOID using public fields, because it's not safe. You shouldn't let anyone from outside modify them directly like this. Instead use only private fields and use public properties to manage access from the outside!

walther
  • 13,466
  • 5
  • 41
  • 67
  • I agree with the "avoid public fields" part, but not because "it's not safe". What's not "safe" about public fields? – Jcl Jan 31 '15 at 18:39
  • @Jcl, I meant situations for example when the creator wants to expose some value but didn't expect it to be modified from outside, because it could produce bugs. The company hired a new programmer and he didn't know about it, so he changed it, which resulted in numerous bugs throughout the application. Of course, it violates OOP principles etc. Maybe my explanation wasn't that clear, but I hope the OP understood at least the core message. – walther Jan 31 '15 at 19:23
  • Exposing a field publicly means you have no way of knowing when a consumer of the class changes it. Even if you use an [auto property](https://msdn.microsoft.com/en-us/library/bb384054.aspx) like `ID` in your example, you can use proper get/set accessors with validation or set different accessibility later. – Wai Ha Lee Jan 31 '15 at 22:58
  • @WaiHaLee, why post it as a comment to my answer? – walther Jan 31 '15 at 23:01
  • I had meant to tag @Jcl but neglected to. Apologies. – Wai Ha Lee Jan 31 '15 at 23:24
  • This is wrong. Public fields shouldn't be avioded, they should just not be misused. Use private fields instead of public fields if… you actually need private fields. Use public fields when you really know what you are doing. – o0'. Feb 01 '15 at 14:46
  • @Lohoris, can you provide a single example when using a public field (= violating encapsulation) is a good practice? Every source I've ever read/heard was encouraging people to use at least automatic properties and everyone discourages use of public fields... – walther Feb 01 '15 at 15:19