Автоматические свойства и структуры не смешиваются?

Нажав несколько небольших структур, отвечая на этот пост, я неожиданно обнаружил следующее:

Следующая структура, использующая поле int, совершенно легальна:

struct MyStruct
{ 
    public MyStruct ( int size ) 
    { 
        this.Size = size; // <-- Legal assignment.
    } 

    public int Size; 
}

Однако следующая структура, использующая автоматическое свойство, не компилируется:

struct MyStruct
{ 
    public MyStruct ( int size ) 
    { 
        this.Size = size; // <-- Compile-Time Error!
    } 

    public int Size{get; set;}
}

Возвращенная ошибка: "Объект 'this' не может использоваться до того, как все его поля будут присвоены". Я знаю, что это стандартная процедура для struct: поле поддержки для любого свойства должно быть назначено напрямую (а не через аксессуар набора свойств) из конструктора struct.

Решение состоит в том, чтобы использовать явное поле поддержки:

struct MyStruct
{ 
    public MyStruct(int size)
    {
        _size = size;
    }

    private int _size;

    public int Size
    {
        get { return _size; }
        set { _size = value; }
    }
}

(Обратите внимание, что VB.NET не будет иметь эту проблему, потому что в VB.NET все поля автоматически инициализируются до 0/null/false при первом создании.)

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

Это второстепенная проблема, почти крайность, но мне было интересно, что другие подумали об этом...

Ответ 1

С С# 6 вперед: это уже не проблема


Becore С# 6, вам нужно вызвать конструктор по умолчанию для этого:

public MyStruct(int size) : this()
{
    Size = size;
}

Большая проблема здесь в том, что у вас есть изменяемая структура. Это никогда хорошая идея. Я бы это сделал:

public int Size { get; private set; }

Не технически неизменяемо, но достаточно близко.

В последних версиях С# вы можете улучшить это:

public int Size { get; }

Теперь это может быть назначено только в конструкторе.

Ответ 2

Вы можете исправить это, вызвав сначала конструктор по умолчанию:

struct MyStruct 
{
    public MyStruct(int size) : this() 
    {
        this.Size = size; // <-- now works
    }

     public int Size { get; set; }
}

Ответ 3

Еще одна неясная проблема в этой проблеме - это одна из пятен во временном классе Tuple в Managed Extensibility Framework (через Krzysztof Koźmic):

public struct TempTuple<TFirst, TSecond>
{
    public TempTuple(TFirst first, TSecond second)
    {
        this = new TempTuple<TFirst, TSecond>(); // Kung fu!
        this.First = first;
        this.Second = second;
    }

    public TFirst First { get; private set; }
    public TSecond Second { get; private set; }

(Полный исходный код из Codeplex: Tuple.cs)

Я также отмечаю, что документация для CS0188 обновлена, чтобы добавить:

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

Поэтому я считаю, что официальное руководство состоит в том, чтобы использовать свойства старого стиля в ваших структурах, когда вы сталкиваетесь с этой проблемой, которая, вероятно, менее неясна (и более читаема), чем любая из двух других альтернатив, изученных так далеко.