.NET Framework: Слабо документированная фича инициализаторов C#, граничущая с приличиями.

Решарпер наткнул меня на следующий интересный случай.

Я порылся и ничего не нашел в MSDN.

Это относиться к .NET Framework 3.0 и длалее.

 

Оказывается запись


var classLevelOne = new ClassLevelOne
{
    PropClassLevelTwoInOne =
    {
        ClassLevelThreeInTwo =
new ClassLevelThree()
    }
};
Не тоже самое, что запись

var classLevelOne = new
ClassLevelOne
{
    PropClassLevelTwoInOne = new PropClassLevelTwo
    {
        ClassLevelThreeInTwo =
new ClassLevelThree()
    }
};

 

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

Как я понимаю, объект в этом свойстве нужно задать ранее в конструкторе или в этом же "скобочном" инициализаторе.

 

Я был немного шокирован, но дебаггинг не дал соврать, потому что выпадает исключение, если свойтсво PropClassLevelTwoInOne ссылается на null.

 

А ответ удалось найти в спецификации языка C#.

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

 

A member initializer that specifies an object initializer after the equals sign is an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the object initializer are treated as assignments to members of the field or property. A property of a value type cannot be initialized using this construct.

 

A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property. The field or property must be of a collection type that satisfies the requirements specified in §26.4.2.

 

the following construct can be used to initialize the embedded Point instances instead of assigning new instances:

var r = new Rectangle {
   P1 = { X = 0, Y = 1 },
   P2 = { X = 2, Y = 3 }
};

which has the same effect as

var r = new Rectangle();
r.P1.X = 0;
r.P1.Y = 1;
r.P2.X = 2;
r.P2.Y = 3;

 
Другими словами, такой конструкцией можно задавать члены объекта в уже инициализированном свойстве.

Свойство должно уже быть ранее определено в конструкторе или в том же "скобочном" инициализаторе.

 

Фича действительно очень удобная и чувствовалось, что ее не хватало, а она оказывается есть.

Но! Мы получили еще одно уязвимое место, плодящее скрытые ошибки при изменении типов и, как минимум, все такие мест обязаны быть покрыты тестами.
Также такой код слабо читаем и различим, что опять ведет к ошибкам.

 

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

_________________


namespace

ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var classLevelOne = new ClassLevelOne

{


PropClassLevelTwoInOne =


{


ClassLevelThreeInTwo =

new ClassLevelThree()

}


};


}


}


internal class ClassLevelOne

{


private ClassLevelTwo _propClassLevelTwoInOne;

public ClassLevelTwo PropClassLevelTwoInOne

{


get

{


return _propClassLevelTwoInOne;

}


set

{


_propClassLevelTwoInOne =

value;

}


}


}


internal class ClassLevelTwo
{
private ClassLevelThree _classLevelThreeInTwo;
public ClassLevelThree ClassLevelThreeInTwo
{
get
{
return _classLevelThreeInTwo;
}
set
{
_classLevelThreeInTwo =
value;
}
}
}
internal class ClassLevelThree
{
}
}
_________________

Комментарии

Популярные сообщения из этого блога

A4tech. Мышь не найдена. Пожалуйста, подсоедините мышь.

SVN: Пропали иконки TortoiseSVN.

Вывод в cmd или bat пустой строки.