Назначить для инициализатора интерфейса массива компиляции, но почему?

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

Прежде чем я начал проверять, что происходит, я написал код, подобный приведенному ниже. К моему развлечению он скомпилирован, я использую .net framework 4, и я знаю, что не существует интерфейса для реализации анонимных объектов, но я не видел жалобы от VS об этом коде.

Еще лучше, когда я устанавливаю брекеты, что intelisense находит "Свойство" моего интерфейса, точно так же, как это было бы правильным кодом.

Почему эта компиляция компилируется, и когда она запускается, она дает исключение ссылочной ссылки?

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            Holder holder = new Holder { someInterface = { Property = 1 } };
            Console.WriteLine(holder.someInterface.Property);
        }
    }

    class Holder
    {
        public ISomeInterface someInterface{get; set;}
    }

    interface ISomeInterface
    {
        int Property { get; set; }
    }
}

Ответ 1

Holder holder = new Holder { someInterface = { Property = 1 } };//<--Note you missed new keyword

Выше строки равно

Holder temp = new Holder();
temp.someInterface.Property = 1;
Holder holder = temp;// <--Here someInterface is null so you get null reference exception. 

Это должно быть что-то вроде

Holder holder = new Holder { someInterface = new SomeClass(){ Property = 1 } };//<--Note the new keyword here

Примечание. Ваш код никогда не вводил "Анонимный тип". Это "Инициализатор объектов".

Когда вы используете синтаксис ObjectInitializer с ключевым словом new, это означает, что вы устанавливаете что-то, когда используете синтаксис ObjectInitializer без ключевого слова new, это означает, что вы читаете что-то.

Ответ 2

было бы аккуратно создавать анонимный объект, который является типом некоторого интерфейса,

Я знаю, что не существует интерфейса для реализации анонимных объектов, но я не видел жалобы от VS об этом коде.

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

new Holder { someInterface = { Property = 1 } };

{ Property = 1 } не создает новый экземпляр анонимного типа - это инициализатор объекта.

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

new Holder { someInterface = new { Property = 1 } };

Ответ 3

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

Holder temp = new Holder(); //creates temp object calling default constructor
temp.someInterface = yourValue;
holder = temp; //finally assigned back to your variable.

В вашем случае someInterface остается неинициализированным. Но это не обязательно, если у вас есть пустой конструктор, инициализирующий someInterface правильно.

class Holder
{
    public Holder()
    {
       someInterface = new Class();
    }

    public ISomeInterface someInterface{get; set;}
}

Теперь это работает:

Holder holder = new Holder { someInterface = { Property = 1 } };

Ответ 4

Вы должны назначить экземпляр someInterface в конструкторе Holder; в противном случае он будет равен нулю.