Почему инициализатор коллекции без `нового` разрешен внутри инициализатора объекта, но не снаружи?

#c# #object-initializers #collection-initializer

#c# #инициализаторы объектов #коллекция-инициализатор

Вопрос:

Я заметил странное поведение при инициализации свойства коллекции.

Рассмотрим:

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

Я могу инициализировать Ints так:

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

Но я не могу этого сделать:

 var x = new X();

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

Есть идеи, почему? Это кажется довольно неинтуитивным.

Комментарии:

1. «потому что так сказано в спецификации языка»

2. @Sweeper Спасибо, но это похоже на ответ на все. Что-то более конкретное было бы более полезным 🙂

3. Подробности об этом синтаксическом сахаре см. В разделе Инициализаторы объектов и коллекций .

4. Хотите, чтобы я указал вам, где именно в спецификации это сказано?

5. Я бы хотел это увидеть 🙂

Ответ №1:

new X ... является началом выражения создания объекта. В такого рода выражениях разрешен инициализатор объекта или коллекции:

 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
    ;
  

В вашем коде у вас есть инициализатор объекта { Ints = ... } . Внутри этого есть другой инициализатор коллекции { 12, 3, 4, 5, 6 } . Это разрешено в соответствии с грамматикой:

 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 может быть либо выражением, либо другим object_or_collection_initializer . Это также подразумевает, что, хотя они могут выглядеть так, object_or_collection_initializer , т. Е. Такие вещи, как { 12, 3, 4, 5, 6 } , не являются своего рода выражением.

С другой стороны, назначения не позволяют этого. Присваивания позволяют выражению находиться только с правой стороны:

 assignment
    : unary_expression assignment_operator expression
    ;
  

Ответ №2:

Как указано в комментариях, это пример инициализатора коллекции.

Это действительно синтаксический сахар для чего-то вроде следующего:

 var theObject = new X();
theObject.Items.Add(12);
theObject.Items.Add(3);
theObject.Items.Add(4);
theObject.Items.Add(5);
theObject.Items.Add(6);
  

Таким образом можно инициализировать все, что соответствует правильной «форме». (По сути, он ищет Add(...) метод, соответствующий правильной сигнатуре ваших типов.) Вы даже можете использовать это для инициализации коллекций словарей.