Search code examples
c#object-initializerscollection-initializer

Why is a collection initializer without `new` allowed inside an object initializer but not outside?


I noticed strange behaviour when initializing collection property.

Consider:

class X
{
    public IList<int> Ints { get; set; }
}

I can initialize Ints like that:

var theObject = new X
{
    Ints = { 12, 3, 4, 5, 6 }
};

But I cannot do that:

var x = new X();

x.Ints = { 12, 3, 4, 5, 6 }

Any ideas why? It seems pretty unintuitive.


Solution

  • new X ... is the start of an object creation expression. In this kind of expressions, an object or collection initializer is allowed:

    object_creation_expression
        : 'new' type '(' argument_list? ')' object_or_collection_initializer?
        | 'new' type object_or_collection_initializer  // <--- here!
        ;
    
    object_or_collection_initializer
        : object_initializer
        | collection_initializer
        ;
    

    In your code, you have an object initialiser { Ints = ... }. Inside that, there is another collection initialiser { 12, 3, 4, 5, 6 }. This is allowed, as per the grammar:

    object_initializer
        : '{' member_initializer_list? '}'
        | '{' member_initializer_list ',' '}'
        ;
    
    member_initializer_list
        : member_initializer (',' member_initializer)*
        ;
    
    member_initializer
        : initializer_target '=' initializer_value
        ;
    
    initializer_target
        : identifier
        | '[' argument_list ']'
        ;
    
    initializer_value
        : expression
        | object_or_collection_initializer // <---- here!
        ;
    

    An initializer_value can either be an expression, or another object_or_collection_initializer. This also implies that, though they may look like it, object_or_collection_initializer, i.e. things like { 12, 3, 4, 5, 6 }, are not a kind of expression.

    On the other hand, assignments don't allow this. Assignments only allow an expression to be on the right hand side:

    assignment
        : unary_expression assignment_operator expression
        ;