2

I have a class with that has a collection-property:

class MyClass
{
    public MyCollection Coll { get; private set; }
    public MyClass() { this.Coll = new MyCollection(); }
}

class MyCollection : IList { ... }

Now I´m creating two different instances of my class:

var o1 = new MyClass() { Coll = {"1", "2"}};
var o2 = new MyClass() { Coll = new MyCollection() {"1", "2"} };

I understand that compiler complains about the latter as the setter for the property doesn´t exist (or in this case isn´t publicly accessable). The first however is an assignement as well - albeit a collection initializer.

I assume that collection-initializers are allowed for get-only properties as they´re just calling Add on IList and do not actually call the getter of the property. Is this right?

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • Does `o1` run, or do you see a run-time error when calling `Add` on a `null` object? – Sergey Kalinichenko Aug 05 '16 at 15:01
  • 1
    Although flagging as duplicate to an [older question](http://stackoverflow.com/questions/5646285/c-sharp-object-initialization-of-read-only-collection-properties), vote for this one as much more concise. – Dmitry Egorov Aug 05 '16 at 15:22

1 Answers1

2

From your question:

I assume that collection initializers are [...] just calling Add on IList [...].

This assumption is correct. Collection initializers are syntactic sugar that the C# compiler translates into something more explicit during compilation. For instance, the following line:

var l = new List<int>() { 1, 2 };

Is actually translated to:

var l = new List<int>();
l.Add(1);
l.Add(2);

You can verify this be looking at the generated MSIL (slightly simplified):

newobj      List<System.Int32>..ctor // Create list object.
stloc.0                              // Store list object as local variable "0".
ldloc.0                              // Push local variable "0" onto the stack.
ldc.i4.1                             // Push 4-byte integer constant "1" onto the stack.
callvirt    List<System.Int32>.Add   // Call "Add" (uses and pops the last two values from
                                     // the stack).
ldloc.0                              // Push list onto stack again.
ldc.i4.2                             // Push constant "2" onto stack.
callvirt    List<System.Int32>.Add   // Call "Add" again.

This means that your code var o1 = new MyClass() { Coll = {"1", "2"}}; never accesses the private setter. It simply gets Coll and calls Add on it like so:

var o1 = new MyClass();
o1.Coll.Add("1");
o1.Coll.Add("2");

Again we can check the MSIL to verify this:

newobj      MyClass..ctor
stloc.1                              // Store MyClass instance at local variable "1".
ldloc.1                              // Load MyClass instance onto stack.
callvirt    MyClass.get_Coll         // Call getter of "Coll" and push the MyCollection
                                     // instance onto the stack.
ldstr       "1"                      // Push the string "1" onto the stack...
callvirt    MyCollection.Add         // ...and call "Add".
pop                                  // Discard the return value of "Add".
ldloc.1
callvirt    MyClass.get_Coll
ldstr       "2"
callvirt    MyCollection.Add
pop
Good Night Nerd Pride
  • 8,245
  • 4
  • 49
  • 65