Тэги

Silverlight (36) WPF (10) IIS (7) Visual Studio (7) SharePoint (6) .Net Framework (5) ODP.NET (5) ASP.NET (4) C# (4) common (4) Network Settings (3) JavaScript (2) MS Office (2) Resharper (2) WCF (2) WEB (2) XPath (2) XSLT (2) ADO.NET (1) APEX (1) CMD (1) CSS (1) EF (1) HTML (1) Hardware bugs (1) Java (1) MS SQL (1) Oracle (1) PDF (1) Version Control (1) XAML (1)

вторник, 8 июня 2010 г.

.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
{
}
}
_________________

WPF: Баг проекта WPF в Visual Studio при работе с ресурсными файлами.

Речь о проекте WPF в Visual Studio.

 

Есть файлы которые компилируются в сборку. Ресурсные файлы.

У них в свойствах в Build Action стоит Resource.

С ними нужно быть осторожным, т.к. при удалении такого файла из проекта или при изменении Build Action на None ничего не меняется.

Грубо говоря этот файл останется у вас в ресурсе. Возможно если вы подмените этот файл, то новый файл не подхватиться (не пробовал).

 

Для решения этой проблемы, после каждого изменения с ресурсными файлами, выполните очистку проекта или всего солюшена - Clean из контекстного меню в Solution Explorer.

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

 

Видел это в VS 2008, в 2010 не пробовал.

Я думаю это относится не только к проектам WPF.