Автоматические свойства и структуры не смешиваются?
Нажав несколько небольших структур, отвечая на этот пост, я неожиданно обнаружил следующее:
Следующая структура, использующая поле 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 обновлена, чтобы добавить:
Если вы видите эту ошибку при попытке инициализировать свойство в структуре конструктор, решение состоит в том, чтобы изменить параметр конструктора для указания фоновое поле вместо самого имущества. Авто Реализуемый свойства следует избегать в структур, потому что у них нет поддержки поле и поэтому не может быть инициализируется каким-либо образом из конструктор.
Поэтому я считаю, что официальное руководство состоит в том, чтобы использовать свойства старого стиля в ваших структурах, когда вы сталкиваетесь с этой проблемой, которая, вероятно, менее неясна (и более читаема), чем любая из двух других альтернатив, изученных так далеко.