Этот комментарий и еще один комментарий на Github ближе всего подходит к рассмотрению текущего положения дел, указывая, почему определение структуры не было разрешено и, вероятно, не будет разрешено в обозримом будущем.
Даже для статического члена требуется, чтобы тип был инициализирован, прежде чем его можно будет включить в макет типа, но для инициализации типа требуется, чтобы этот статический член был инициализирован. Этот цикл зависимостей инициализации создает Catch-22, который приводит к исключению во время выполнения.
Согласно этому комментарию, .NET Core отлично работает с этим шаблоном. Но я обнаружил ту же ошибку, когда попробовал ваш пример в проекте .NET Core. Так что либо этот комментарий ошибочен, либо между сценарием экземпляра-члена и вашим сценарием статического члена есть какая-то тонкая разница (я не удосужился исследовать дальше этого).
Интересно, что компилятор, используемый на dotNETFiddle.net, выдает ошибку времени компиляции, Член структуры "поле struct2" типа "struct1" вызывает цикл в макете структуры. Я не знаю, почему компилятор Visual Studio больше не выдает эту ошибку (проверено в 2017 и 2019 годах). Мне кажется еще одна ошибка. Но обсуждение этой проблемы на Github, похоже, признает, что код технически действителен (т. е. соответствует спецификации C#), поэтому, вероятно, в какой-то момент было принято сознательное решение удалить ошибку компилятора и позволить CLR подавать жалобы в время выполнения.
Обратите внимание, что совет на странице с описанием ошибки предлагает изменить на class
вместо struct
. Конечно, это часто невозможно, когда используется struct
; может быть важно иметь тип значения. Однако в вашем конкретном примере на самом деле есть простой обходной путь, основанный на этой идее. Поскольку ваше поле не является частью фактического макета экземпляра struct
, вы можете переместить его в статический класс, используемый специально для таких значений. Например.:
public struct Point<T>
{
public static class Constants
{
static Point<int> IntOrigin = new Point<int>(0, 0);
}
T X { get; }
T Y { get; }
public Point(T x, T y)
{
this.X = x;
this.Y = y;
}
}
Тогда вместо (например) Point<double>.IntOrigin
вам нужно будет использовать Point<double>.Constants.IntOrigin
. Поскольку инициализацию типа для каждого типа можно выполнить независимо, цикл при инициализации не возникает и код работает нормально.
person
Peter Duniho
schedule
12.09.2020