3

So I was looking at the documentation to understand how to use an object initializer, be it for anonymous types or not. The only thing that I would like to find out is why(and if it matters) is there a difference in the example.

It goes like this: for a Cat

class Cat
{
    public int Age { get; set; }
    public string Name { get; set; }
}

I can see the first example like so:

Cat cat = new Cat { Age = 10, Name = "Fluffy" };

This makes sense. But then, you can find the following:

List<Cat> cats = new List<Cat>
{
    new Cat(){ Name = "Sylvester", Age=8 },
    new Cat(){ Name = "Whiskers", Age=2 }
};

Now my question is: (why) is there a difference between new Cat{...} and new Cat(){...} ? Why should we use(or not) the parentheses?

Rand Random
  • 7,300
  • 10
  • 40
  • 88
knee pain
  • 600
  • 3
  • 19
  • I wish such "optimizations" of a language should take a time to consider the balance between advantages -- spares you from adding `()` -- and disadvantages -- the confusion and all fuss it draws plus a new rule to remember that you can only use this syntactic candy when the class has a parameterless constructor. As a side note, in C++ it has a purpose: zero initialization vs indeterminate value. – mireazma Apr 17 '21 at 10:12

2 Answers2

6

These are just two acceptable equivalent syntax for instantiation of objects in C# with object initialization (you can't omit the parenthesis if you are just doing var cat = new Cat();).

  • since your class has no explicit constructor, a default parameter-less constructor is provided to the Cat class
  • new Cat {...} allows to instantiate and initialize properties of the Cat object as a shortcut for new Cat() {...}, calling the mentioned constructor.

The part about the constructor is important. If there is no implicit default constructor / explicit parameter-less constructor, then you cannot omit the parenthesis, and you'll have to provide parameters in them anyway :

public class Cat {
    public string Name;
    public int Age;

    public Cat(string s) { // since I provide a constructor with parameter here, no parameterless constructor exists
        Name = s; 
    } 
}

// ...
void TestCat() 
{

    // compilation error : 'Cat'' does not contain a constructor that takes 0 arguments
    //var badCat1 = new Cat { Name = "Felix", Age = 3} ;
    //var badCat2 = new Cat() { Name = "Felix", Age = 3} ;

    // works (but no way to remove parenthesis here, since there are parameters to pass to csontructor)
    var goodCat = new Cat("Felix") { Age = 3 } ;

    Console.WriteLine($"The cat {goodCat.Name} is {goodCat.Age} years old");
}

Special case : (which is used a lot for collections, list, dictionaries, etc...).

If a class T implements IEnumerable (i.e. has a IEnumerable GetEnumerator() public function), and implements an Add method, then the object initializer will use the Add method with the collection on enumeration.

Example from https://blog.mariusschulz.com/2014/06/26/fun-with-custom-c-collection-initializers

Creation of special class "Points" which acts like a "List" with the initialization.

Note that this also uses the existing parameter-less constructor !

public class Points : IEnumerable<Point3D>
{
    private readonly List<Point3D> _points;

    public Points()
    {
        _points = new List<Point3D>();
    }

    public void Add(double x, double y, double z)
    {
        _points.Add(new Point3D(x, y, z));
    }

    public IEnumerator<Point3D> GetEnumerator()
    {
        return _points.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Used like that :

var cube = new Points
{
    { -1, -1, -1 },
    { -1, -1,  1 },
    { -1,  1, -1 },
    { -1,  1,  1 },
    {  1, -1, -1 },
    {  1, -1,  1 },
    {  1,  1, -1 },
    {  1,  1,  1 }
};
Pac0
  • 21,465
  • 8
  • 65
  • 74
  • 1
    For any downvoter, please explain how this is wrong, so I can improve and/or delete my answer. – Pac0 Jan 29 '18 at 11:52
  • You might want to go a bit more into detail regarding the questions at the bottom: *"(why) is there a difference between new Cat{...} and new Cat(){...} ? Why should we use(or not) the parentheses?"* – Manfred Radlwimmer Jan 29 '18 at 11:56
  • 1
    @ManfredRadlwimmer thanks for your input. I added more details, and trying not to be too redundant with the other accepted answer. – Pac0 Jan 29 '18 at 12:06
  • 1
    Upvoted, for mentioning what's behind the scenes for the collections initializers – Camilo Terevinto Jan 29 '18 at 12:28
6

If an object has a parameterless constructor, you can omit the parentheses. So both of these are valid

// Assuming cat has a constructor with no parameters
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat cat = new Cat() { Age = 10, Name = "Fluffy" };

The List<T> itself has an object initializer where you can provide any number of items, and they are automatically added to the collection.

List<Cat> cats = new List<Cat>()
{
    new Cat(){ Name = "Sylvester", Age=8 }, // Add sylvester to the List
    new Cat(){ Name = "Whiskers", Age=2 } // And Whiskers too
};

As mentioned above you can also remove the parentheses here aswell

List<Cat> cats = new List<Cat>
{
    new Cat { Name = "Sylvester", Age=8 }, // Add sylvester to the List
    new Cat { Name = "Whiskers", Age=2 } // And Whiskers too
};
Rand Random
  • 7,300
  • 10
  • 40
  • 88
Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • I think your reference to the parameterless constructor (name) was pretty important, since it's used to specify error messages in different libraries like EF – Camilo Terevinto Jan 29 '18 at 11:55
  • @Jamiec , it seems that this is the part I was missing: *If an object has a no parameter constructor, you can omit the parentheses* – knee pain Jan 29 '18 at 12:01
  • @CamiloTerevinto, can you elaborate a bit on what you've just pointed? – knee pain Jan 29 '18 at 12:01
  • 1
    @bgiuga A constructor that takes no parameters is called a *parameterless constructor*. It's important to know the name because there are libraries that refer explicitly to them when throwing error messages – Camilo Terevinto Jan 29 '18 at 12:03