В Visual Studio 2015 отсутствует новое ключевое слово - ошибка компилятора
Я работал над одним из наших новых приложений, когда совершил глупую ошибку… Я забыл написать: "new ClassName" внутри инициализатора объекта. Странно, VS просто скомпилировал код... без каких-либо ошибок или предупреждений. (Я использовал VS 2015, .Net 4.5.2)
Мой код:
var target = new MyClass
{
MyValues = new List<MyOtherClass>
{
new MyOtherClass
{
Start = new DateTimeValue
{
DateTime = new DateTime(2015, 08, 23)
},
End = {
DateTime = new DateTime(2015, 08, 23)
}
}
}
};
(Start и End имеют тип DateTimeValue)
Когда я запускаю приложение, этот код вызывает исключение NullReferenceException. (Добавление "нового DateTimeValue" решило проблему). Почему не было ошибки от компилятора?
2 ответа
Там нет ничего плохого там вообще. Он использует инициализатор объекта для End
который устанавливает свойства, используя существующее значение вместо назначения нового значения End
, Ваш код эквивалентен:
var tmp0 = new MyClass();
var tmp1 = new List<MyOtherClass>();
var tmp2 = new MyOtherClass();
var tmp3 = new DateTimeValue();
tmp3.DateTime = new DateTime(2015, 8, 23);
// Note the assignment to Start here
tmp2.Start = tmp3;
// No assignment to End -
// it's calling the End "getter" and then the DateTime "setter" on the result
tmp2.End.DateTime = new DateTime(2015, 8, 23);
tmp1.Add(tmp2);
tmp0.MyValues = tmp1;
var target = tmp0;
Из спецификации C# 5, раздел 7.6.10.2:
Инициализатор члена, который указывает инициализатор объекта после знака равенства, является инициализатором вложенного объекта, то есть инициализацией внедренного объекта. Вместо назначения нового значения полю или свойству, назначения в инициализаторе вложенного объекта обрабатываются как присвоения членам поля или свойства.
Это именно то, что вы делаете здесь - { DateTime = ... }
часть является инициализатором объекта.
Потому что это правильный синтаксис.
Это примерно означает следующее:
var target = new MyClass();
target.MyValues = new List<MyOtherClass>();
var i = new MyOtherClass();
i.Start = new DateTimeValue();
i.Start.DateTime = new DateTime(2015, 08, 23);
i.End.DateTime = new DateTime(2015, 08, 23);
target.MyValues.Add(i);
За исключением того, что свойства устанавливаются только один раз и никогда не читаются компилятором. Это потребовало бы использования дополнительных временных переменных (как Джон сделал в своем ответе), но я не стал писать их здесь для простоты.
Этот синтаксис полезен для случая, когда MyOtherClass
создает экземпляр End
Свойство в своем конструкторе. Его также можно использовать, когда свойство доступно только для чтения.