Почему необходимо вызывать this () для структуры, чтобы использовать автоматические свойства в c #? - PullRequest
53 голосов
/ 07 ноября 2008

Если я определю структуру в C #, используя автоматические свойства, подобные этому:

public struct Address
{
    public Address(string line1, string line2, string city, string state, string zip)
    {
        Line1 = line1;
        Line2 = line2;
        City = city;
        State = state;
        Zip = zip;
    }

    public string Line1 { get; protected set; }
    public string Line2 { get; protected set; }
    public string City { get; protected set; }
    public string State { get; protected set; }
    public string Zip { get; protected set; }
}

Когда я пытаюсь собрать файл, я получаю сообщение об ошибке компиляции: The 'this' object cannot be used before all of its fields are assigned to. Эту проблему можно решить, изменив конструктор так, чтобы он вызывал цепочечный вызов конструктора по умолчанию, например:

public Address(string line1, string line2, string city, string state, string zip): this()
{
    Line1 = line1;
    Line2 = line2;
    City = city;
    State = state;
    Zip = zip;
}

Мой вопрос: почему это работает и что происходит? У меня есть предположение, и я пытался доказать это, глядя на ИЛ, но я шучу, только если думаю, что могу сломать ИЛ. Но я предполагаю, что автоматические свойства работают, когда компилятор создает поля для ваших свойств за кулисами. К этим полям нельзя получить доступ через код, все настройки и получение должны быть сделаны через свойства. При создании структуры конструктор по умолчанию не может быть определен явно. Таким образом, за кулисами компилятор должен генерировать конструктор по умолчанию, который устанавливает значения полей, которые разработчик не может видеть.

Любые и все волшебники IL могут доказать или опровергнуть мою теорию.

Ответы [ 2 ]

52 голосов
/ 07 ноября 2008

Примечание: начиная с C # 6, это не требуется, но вы все равно должны использовать автоматически реализуемые свойства только для чтения с C # 6 ...

this() гарантирует, что поля определенно назначены для компилятора - он устанавливает для всех полей их значения по умолчанию. Чтобы получить доступ к любым свойствам, у вас должна быть полностью построенная структура.

Это раздражает, но так оно и есть. Вы действительно хотите, чтобы это была структура? И зачем использовать защищенный установщик на структуру (которая не может быть получена из)?

0 голосов
/ 01 октября 2012

Свойство - это не что иное, как инкапсуляция метода Get и / или метода Set. В CLR есть метаданные, которые указывают, что определенные методы следует рассматривать как свойства, то есть компиляторы должны разрешать некоторые конструкции, которые не допускаются в методах. Например, если X является свойством чтения-записи Foo, компилятор переведет Foo.X += 5 в Foo.SET_X_METHOD(Foo.GET_X_METHOD() + 5) (хотя методы называются по-разному и обычно не доступны по имени).

Хотя autoproperty реализует пару методов get / set, которые обращаются к приватному полю таким образом, чтобы вести себя более или менее как поле, с точки зрения любого кода вне свойства, autoproperty - это пара методов get / set, как и любое другое свойство. Следовательно, утверждение типа Foo.X = 5; переводится как Foo.SET_X_METHOD(5). Поскольку компилятор C # воспринимает это как вызов метода, а методы не содержат метаданных, указывающих, какие поля они читают или пишут, компилятор запрещает вызов метода, если ему не известно, что каждое поле Foo было записано.

Лично я бы советовал избегать использования autoproperties с типами структур. Автозапчасти имеют смысл с классами, поскольку свойства классов могут поддерживать такие функции, как уведомления об обновлениях. Даже если ранние версии класса не поддерживают уведомления об обновлениях, наличие в этих версиях автоматического свойства, а не поля будет означать, что в будущих версиях можно будет добавлять функции уведомлений об обновлениях, не требуя переделки пользователей класса. Структуры, однако, не могут значимо поддерживать большинство типов функций, которые можно добавить в свойства, подобные полям.

Кроме того, различия в производительности между полями и свойствами намного больше для больших структур, чем для типов классов. Действительно, большая часть рекомендации избегать больших структур является следствием этого различия. Большие структуры на самом деле могут быть очень эффективными, если избежать ненужного их копирования. Даже если бы у вас была огромная структура HexDecet<HexDecet<HexDecet<Integer>>>, где HexDecet<T> содержал открытые поля F0 .. F15 типа T, для оператора, подобного Foo = MyThing.F3.F6.F9;, просто потребовалось бы прочитать одно целое число из MyThing и сохранить это к Foo, хотя MyThing было бы огромным по структурным стандартам (4096 целых чисел, занимающих 16 КБ). Кроме того, можно очень легко обновить этот элемент, например, MyThing.F3.F6.F9 += 26;. Напротив, если бы F0 .. F15 были автоматическими свойствами, для оператора Foo = MyThing.F3.F6.F9 потребовалось бы скопировать 1 КБ данных из MyThing.F3 во временный (назовите его temp1, затем 64 байта данных из * 1031) * до temp2) прежде чем, наконец, приступим к чтению 4 байтов данных из temp2.F9. Ик. Хуже того, попытка добавить 26 к значению в MyThing.F3.F6.F9 потребует что-то вроде var t1 = MyThing.F3; var t2 = t1.F6; t2.F9 += 26; t1.F6 = f2; MyThing.F3 = t1;.

Многие из давних жалоб на «изменяемые типы структур» на самом деле являются жалобами на типы структур со свойствами чтения / записи. Просто замените свойства полями, и проблемы исчезнут.

PS: Иногда бывает полезно иметь структуру, свойства которой обращаются к объекту класса, на который она ссылается. Например, было бы неплохо иметь версию класса ArraySegment<T>, которая позволяла бы сказать Var foo[] = new int[100]; Var MyArrSeg = New ArraySegment<int>(foo, 25, 25); MyArrSeg[6] += 9;, и чтобы последний оператор добавил девять к элементу (25 + 6) из foo. В старых версиях C # это можно было сделать. К сожалению, частое использование autoproperties в Framework, где поля были бы более подходящими, привело к распространенным жалобам на компилятор, позволяющий бесполезно вызывать установщики свойств в структурах только для чтения; следовательно, вызов любого установщика свойства в структуре только для чтения теперь запрещен независимо от того, будет ли установщик свойства фактически изменять какие-либо поля структуры. Если бы люди просто воздерживались от создания структур, изменяемых с помощью установщиков свойств (делая поля непосредственно доступными, когда изменчивость была подходящей), компиляторам никогда бы не пришлось реализовывать это ограничение.

...