Автоматические свойства и структуры несовместимы?
-
05-07-2019 - |
Вопрос
Передвигаясь по небольшим конструкциям во время ответа эта почта, я неожиданно наткнулся на следующее:
Следующая структура с использованием поля 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' нельзя использовать, пока не будут назначены все его поля».Я знаю, что это стандартная процедура для структуры:резервное поле для любого свойства должно быть назначено напрямую (а не через метод доступа set свойства) из конструктора структуры.
Решение состоит в том, чтобы использовать явное резервное поле:
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 при первом создании.)
Казалось бы, это досадное ограничение при использовании автоматических свойств со структурами в C#.Размышляя концептуально, мне было интересно, не было бы разумным местом для исключения, позволяющего вызывать метод доступа к набору свойств внутри конструктора структуры, по крайней мере, для автоматического свойства?
Это незначительная проблема, почти крайний случай, но мне было интересно, что об этом думают другие...
Решение
Начиная с C # 6: это больше не проблема
<Ч>Поскольку C # 6, вам нужно вызвать конструктор по умолчанию, чтобы это работало:
public MyStruct(int size) : this()
{
Size = size;
}
Большая проблема в том, что у вас есть изменяемая структура. Это никогда хорошая идея. Я бы сделал это:
public int Size { get; private set; }
Не технически неизменный, но достаточно близко.
В последних версиях C # вы можете улучшить это:
public int Size { get; }
Теперь это можно только назначать в конструкторе.
Другие советы
Это можно исправить, сначала вызвав конструктор по умолчанию:
struct MyStruct
{
public MyStruct(int size) : this()
{
this.Size = size; // <-- now works
}
public int Size { get; set; }
}
Еще один малоизвестный способ решения этой проблемы найден во временном каталоге. Tuple
класс в Платформа управляемой расширяемости (с помощью Кшиштоф Козмич):
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: Кортеж.cs)
Также отмечу, что документация по CS0188 было обновлено, чтобы добавить:
Если вы видите эту ошибку при попытке инициализации свойства в конструкторе структуры, решение состоит в том, чтобы изменить параметр конструктора, чтобы указать поле поддержки вместо самого свойства. Аутоизогнированные свойства следует избегать в структурах, потому что они не имеют поля поддержки и, следовательно, не могут быть инициализированы каким-либо образом из конструктора.
Я считаю, что это означает, что официальное руководство состоит в том, чтобы использовать свойства старого стиля в ваших структурах, когда вы сталкиваетесь с этой проблемой, которая, вероятно, менее неясна (и более читабельна), чем любой из двух других альтернатив, рассмотренных до сих пор.