#c_sharp #net
Троелсен пишет: На заметку! Вы наверняка заметили в конструкторах Square и Rectangle связывание в цепочку со стандартным конструктором. Причина в том, что при наличии структуры, которая использует синтаксис автоматических свойств (как в данном случае), стандартный конструктор должен быть явно вызван (из всех специальных конструкторов) для инициализации закрытых поддерживающих полей. Да, это весьма своеобразное правило C#, но, в конце концов, ведь данная глава посвящена сложным темам. И приводит в пример такой код: public struct Rectangle { public int Width { get; set; } public int Height { get; set; } public Rectangle(int w, int h) : this() { Width = w; Height = h; } public void Draw() { for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { Console.Write("*"); } Console.WriteLine(); } } public override string ToString() { return string.Format("[Width = {0}; Height = {1}]", Width, Height); } } public struct Square { public int Length { get; set; } public Square(int i) : this() { Length = 1; } public void Draw() { for (int i = 0; i < Length; i++) { for (int j = 0; j < Length; j++) { Console.Write("*"); } Console.WriteLine(); } } public override string ToString() { return string.Format("[Length = {0}]", Length); } public static explicit operator Square(Rectangle r) { Square s = new Square(); s.Length = r.Height; return s; } } Немного, не понимаю, зачем нужно вызывать базовый конструктор структур? Попробовал в дебаггере и так, и так вызвать и разницы не заметил...
Ответы
Ответ 1
Кратко и по сути: раньше компилятор был глупее, сейчас это больше не обязательно. Правило насчёт необходимости вызывать конструктор по умолчанию поменялось, начиная с версии C# 6. Есть общее правило: все поля должны быть инициализированы. Оно неизменно. Когда вы вызываете конструктор без параметров, он инициализирует все поля значением по умолчанию (нулём или null, обычно). Теперь смотрите, что происходит. Когда вы пишете public struct Square { public int Length { get; set; } } для свойства Length автоматически создаётся невидимое поле, в котором реально содержится значение свойства. Старые версии языка вот в таком коде: public Square(int i) { Length = 1; } не могли распознать, что невидимое поле инициализируется до окончания конструктора, потому что вы обращаетесь лишь к свойствам. Ведь свойство — это не поле, и компилятор не знал, что именно произойдёт при вызове сеттера свойства! Поэтому было требование вызвать конструктор без параметров, который уж точно инициализировал всё как надо, чтобы компилятор был уверен на все сто. Хуже того: поскольку компилятор не знал, что именно происходит в сеттере, он предполагал, что тот может захотеть читать поле, которое ещё не инициализировано! (Спасибо за замечание @PetSerAl.) Текущая версия компилятора стала умнее, и распознаёт инициализацию автоматически созданных полей через вызов сеттера свойства. Поэтому сейчас вызов конструктора по умолчанию не нужен, если вы инициализируете все свойства в конструкторе. Замечание Троелсена устарело. Обратите внимание, что вы реально должны инициализировать все поля перед использованием. Например, такой код не скомпилируется, и потребует вызова конструктора по умолчанию: public struct Square { public int Length { get; set; } public int Age { get; set; } public Square(int i) { Length = 1; // Age не инициализировано } } И такой: public struct Square { public int Length { get; set; } public int Age { get; set; } public Square(int i) { Length = 1; Length += Age; // Age используется до инициализации Age = 10; } } Кстати, компилятор стал умнее только насчёт автоматически определённых свойств. Если вы определяете свойство вручную, компилятор не пытается анализировать код. Например, вот такое не скомпилируется: public struct Square { int length; public int Length { get { return length; } set { length = value; } } public Square(int i) { Length = 1; } } потому что компилятор не может сообразить, что вы всё же инициализируете поле.Ответ 2
C# 5 и ниже: Дело в том, что параметр this для конструктора структуры ведёт себя так, как если бы был определён как out S this, что означает, что this не является определённо присвоенным, в терминологии спецификации C#, в момент входа в конструктор. В то время как для прочих методов структур параметр this ведёт себя как ref S this. Переменную, которая не является определённо присвоенной, нельзя передавать в методы в качестве параметра ни по значению, ни по ссылке. Получается, что вы не можете вызывать метод назначения свойства (CS0188) до того, как присвоите значения всем полям структуры в конструкторе. Для автоматически реализованных свойств компилятор создаёт скрытое резервное поле для хранения значения, но дотянутся непосредственно до этого поля в пользовательском коде невозможно. Получается, что в конструкторе Вы не можете вызвать метод назначения автоматически реализованного свойства, пока не присвоите резервному полю какое-либо значение. Получается замкнутый круг: чтобы назначить значение скрытому резервному полю, надо вызвать метод назначения свойства, но метод назначения свойства нельзя вызывать пока скрытому резервному полю не присвоено значение. Вызов конструктора по умолчанию используется для того, чтобы этот круг разорвать. C# 6 В C# 6 сделано послабление: в конструкторе разрешено вызывать методы назначения для автоматически реализованных свойств, пока this не является определённо присвоенным, а также методы чтения для свойств, которые являются определённо присвоенными. Послабление не касается свойств с кодом пользователя. Обращение к ним по прежнему считается вызовом метода и недопустимо до полной инициализации this.
Комментариев нет:
Отправить комментарий