Search code examples
c#clr

Can't understand why array initializer in object initializer cannot compiled


I saw NRE when using array initializer in object initializer and there was a update and https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#extension-add-methods-in-collection-initializers but still I cannot understand.

var arr = new int[] { 1, 2, 3 } generate IL code using stelem. But int[] arr = { 1, 2, 3 } generate IL code using RuntimeHelpers.InitializeArray. I think it is using Array.Add extension method which was talk in that answer.

But in object initializer, all array initializer generate second code. An example,

new A() { 
    arr = new int[] { 1, 2, 3 }
}

Array arr is created using RuntimeHelpers.InitializingArray too. Then, doesn't it mean there isn't any problem in next code?

new A() {
    arr = { 1, 2, 3 } // Compiler error!
}

Not like old version of c# compiler, it makes compiler error saying system.array does not contain a definition for Add. What's happening?

EDIT I thought just syntax without new [] makes differences but actually more than three elements makes different IL code.


Solution

  • The second syntax ( { arr = {... } }) is syntax sugar for sequence of value.arr.Add(.) - presumably value.arr is not initialized in your constructor and hence NRE.

    Assuming A is:

    class A { 
         public int[] arr {get;set} 
    }
    

    then

    var x = new A() {
        arr = { 1, 2, 3 } // Compiler error!
    };
    

    is the same as

    var x = new A(); // note x.arr is default null here
    x.arr.Add(1);  // NRE is arr is list, can't compile for int[]
    x.arr.Add(2);
    x.arr.Add(3);
    

    Fix: use list, there is no reasonable way to add elements to an array. If using some other type that does not have .Add - implement extension method that is visible to that code.

    Why version with new works:

    var x = new A() {
        arr = new int[] { 1, 2, 3 } 
    };
    

    is equivalent* of

    var x = new A();
    x.arr = new int[] { 1, 2, 3 };
    

    Note that in array initializer you can use both syntaxes to the same effect, but not in array field's initializers (All possible C# array initialization syntaxes)

    int[] x = { 10, 20, 30 }; // valid, same as int[] x = new int[]{ 10, 20, 30 };
    

    while new A { arr = { 10, 20, 30} } is not the same as new A { arr = new int[3]{ 10, 20, 30} }


    *It is really a bit more complicated to satisfy rules when change must be observable - see Eric Lippert's comment below