Почему эта неправильная инициализация объекта с фигурными скобками вообще компилируется?

При создании некоторых фиктивных данных для коллекции для проекта WPF/MVVM я создал следующий неверный код, который компилируется нормально, но во время выполнения выдает исключение.

Существует вложенная структура объектов данных, которые я ошибочно создаю только с помощью фигурных скобок (похоже, написание JavaScript наносит непоправимый вред мозгу).

using System.Collections.ObjectModel;

namespace testapp
{
    class Program
    {
        static void Main(string[] args)
        {
            var collection = new ObservableCollection<TopLevelDataObject>();
            collection.Add(new TopLevelDataObject{Nested = {Bar = 5}});         // NullReferenceException
        }

        class TopLevelDataObject
        {
            public NestedDataObject Nested { get; set; }
            public string Foo { get; set; }
        }

        class NestedDataObject
        {
            public double Bar { get; set; }
        }
    }
}

Почему это компилируется?

Если я создаю анонимный тип, такой как Nested = new {Bar = 5}, я получаю сообщение об ошибке во время компиляции (что, таким образом, терпит неудачу):

Cannot implicitly convert type '<anonymous type: int Bar>' to 'testapp.Program.NestedDataObject'

Почему я не получаю такой ошибки при пропуске оператора new?

Он даже дает мне подсказку кода для свойства: нажмите, чтобы увидеть изображение, потому что у меня недостаточно представителей для встраивания  это еще

Я предполагаю, что {Bar = 5} - это просто блок кода, который сам по себе является допустимым. Но почему допустимо присваивать кодовый блок чему угодно (в данном случае свойству Nested)?


person surface    schedule 05.03.2018    source источник


Ответы (2)


Почему это компилируется?

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

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

Измените public NestedDataObject Nested { get; set; } на это, чтобы увидеть, как это работает:

public NestedDataObject Nested { get; } = new NestedDataObject();

(Обратите внимание, что вы никогда не сможете присвоить значение Nested вне конструктора в приведенном выше коде!)

person Patrick Hofman    schedule 05.03.2018

Почему это компилируется?

Потому что var x = new SomeType{Property = value}; это то же самое, что:

var x = new SomeType();
x.Property = value;

Действительно, мы можем даже оставить в (), чтобы иметь var x = new SomeType(){Property = value}; или даже var x = new SomeType(argument){Property = value};, сочетая передачу аргумента конструктору и установку значения.

Таким образом, вы можете видеть, что всегда вызывается конструктор, и если вы опустите круглые скобки, которые делают это явным, это всегда конструктор с нулевым значением (без аргументов).

Между тем тип без явного конструктора всегда имеет общедоступный нулевой конструктор («конструктор по умолчанию»).

Следовательно, new TopLevelDataObject{Nested = {Bar = 5}} совпадает с:

var temp = new TopLevelDataObject();
temp.Nested.Bar = 5; // NRE on temp.Nested

Поскольку TopLevelDataObject может иметь конструктор, который устанавливает `Nestedt, тогда код, который у вас есть, может работать, поэтому он должен компилироваться. Конечно, поскольку у него нет такого конструктора, он не работает.

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

person Jon Hanna    schedule 05.03.2018
comment
Как можно было опубликовать ответ 15 минут назад, если вопрос был закрыт 20 минут назад? - person Evk; 05.03.2018
comment
@Evk Мне тоже это интересно. - person Jon Hanna; 05.03.2018
comment
@Evk Есть льготный период, который делает это возможным, для получения дополнительной информации проверьте этот мета-вопрос: meta.stackoverflow.com/q/ 252711/8958148 - person surface; 05.03.2018