Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between these two variations of collection initialiser expressions?

Tags:

I've been using C# for a while, but recently noticed that the behaviour of one of my unit tests changed depending on which variation of collection initialiser expression I used:

  • var object = new Class { SomeCollection = new List<int> { 1, 2, 3 } };
  • var object = new Class { SomeCollection = { 1, 2, 3 } };

Up until this point I assumed that the second form was just syntactic sugar and was semantically equivalent to the first form. However, switching between these two forms resulted in my failing unit test passing.

The example code below demonstrates this:

void Main()
{
    var foo1 = new Foo { Items = new List<int> { 1, 2, 3} };
    var foo2 = new Foo { Items = { 1, 2, 3 } };

    foo1.Dump();
    foo2.Dump();
}

class Foo
{
    public List<int> Items { get; set; }
}

When I run this, the first assignment works fine but the second results in a NullReferenceException.

My gut feeling is that behind the scenes the compiler is treating these two expressions as this:

var foo1 = new Foo();
foo1.Items = new List<int> { 1, 2, 3 }; 

var foo2 = new Foo();
foo2.Items.Add(1);
foo2.Items.Add(2);
foo2.Items.Add(3);

Is that assumption accurate?

like image 944
Tagc Avatar asked Jul 31 '17 07:07

Tagc


1 Answers

Yes, your assumption is accurate. If an object initializer just has:

{
    Property = { ... }
}

rather than

{
    Property = expression
}

then the setter for the property isn't used - the getter is used, and then either the Add method is called, or properties are set within the returned value. So:

var foo = new Foo
{
    Collection = { 1 }
    Property =
    {
        Value = 1
    }
};

is equivalent to:

// Only the *getters* for Collection and Property are called
var foo = new Foo();
foo.Collection.Add(1);
foo.Property.Value = 1;

Compare that with:

var foo = new Foo
{
    Collection = new List<int> { 1 },
    Property = new Bar { Value = 1 }
};

which is equivalent to:

// The setters for Collection and Property are called
var foo = new Foo();
foo.Collection = new List<int> { 1 };
foo.Property = new Bar { Value = 1 };
like image 126
Jon Skeet Avatar answered Oct 13 '22 02:10

Jon Skeet