ковариантные инициализаторы объектов? - PullRequest
6 голосов
/ 23 января 2011

говорят, что у меня есть класс со свойством словарь, используя инициализатор объекта, я могу использовать этот синтаксис (который, я думаю, выглядит довольно чисто):

new MyClass()
{
  Table = { {"test",true},{"test",false} }
}

однако вне инициализатора я не могу сделать это:

this.Table = { {"test",true},{"test",false} };

Почему инициализаторы - это особый случай?Я бы рискнул предположить, что это как-то связано с требованиями LINQ, ковариацией или чем-то еще, но он чувствует себя немного неуместным, потому что не может использовать такой инициализатор везде ...

Ответы [ 3 ]

11 голосов
/ 23 января 2011

Вопрос несколько сбивает с толку, так как этот вопрос не имеет ничего общего с LINQ, не имеет ничего общего с общей дисперсией и включает инициализатор коллекции, а также инициализатор объекта. На самом деле, насколько я могу судить, " почему нельзя использовать инициализатор коллекции вне выражения создания объекта? "

Соответствующий здесь принцип проектирования заключается в том, что в общем случае мы хотим, чтобы операции, которые создают и инициализируют объекты, имели где-то слово «новый» в качестве сигнала читателю, что здесь происходит создание объекта. (Да, в C # есть несколько исключений из этого правила. В качестве упражнения для читателя посмотрите, можете ли вы назвать их все.)

Делая вещи по-своему, становится сложнее рассуждать о коде. Быстро, что это делает?

d = new List<int>() { 10, 20, 30 };
d = { 40, 50, 60 };

Добавляет ли вторая строка 40, 50, 60 к существующему списку? Или он заменяет старый список новым? Там нет «нового», поэтому читатель ожидает, что новый объект был создан?

Когда вы говорите

q = new Whatever() { MyList = { 40, 50, 60 } };

, который не создает новый список; он добавляет 40, 50, 60 к существующему списку, выделенному конструктором. Поэтому предлагаемый вами синтаксис неоднозначен и сбивает с толку вопрос о том, создан новый список или нет.

Предложенная функция является запутанной и ненужной, поэтому вряд ли она будет реализована в ближайшее время.

2 голосов
/ 23 января 2011

Это ограничение намного старше, чем LINQ.Даже в C вы могли написать

int numbers[5] = {1, 2, 3, 4, 5};

, но вы не могли использовать этот синтаксис для присвоения значений массиву.

Я думаю, что причина в C # заключается в том, что обычно вам не следуетне используйте одну и ту же ссылку для двух разных объектов.Если вам нужно присвоить новую коллекцию существующей ссылке, скорее всего, вы разработали свой код не очень хорошо, и вы можете либо инициализировать коллекцию при определении, либо использовать две отдельные ссылки вместо одной.

0 голосов
/ 23 января 2011

Учитывая, что ваш синтаксис выбрасывает NullReferenceException во время выполнения - вы уверены, что можете использовать его?

public class Test
{
  public Dictionary<string, bool> Table {get; set;}
}

public void TestMethod()
{
  Test t = new Test { Table = { {"test", false} } }; //NullReferenceException
}

Это компилируется в следующее (через отражатель):

Test <>g__initLocal3 = new Test();
<>g__initLocal3.Table.Add("test", 0.0M);

Как видите, Table не инициализируется, поэтому во время выполнения выдается NullReferenceException.

Если вы создаете словарь в ctor Test, инициализатор класса создает каскад Add операторы, которые являются синтаксическим сахаром в инициализаторе (для IEnumerable s).

Это, вероятно, не было введено для нормального кода из-за непредвиденных побочных эффектов, которые мы не можем увидеть или представить.Эрик Липперт мог бы помочь, потому что он, вероятно, лучше разбирается в этом вопросе.

...