Почему эта enum-декларация работает сейчас?

Отвечая на другой вопрос, Джон Скит упомянул о том, что в определении enums есть странная вещь. Его ответ.

Он утверждает, что переопределение базового типа enum возможно только с помощью псевдонимов типов, а не с типами фреймов (int допустимо, Int32 нет и т.д.)

public enum Foo : UInt32 {} // Invalid
public enum Bar : uint   {} // Valid

Теперь я попытался воспроизвести это (с С# 6/Roslyn в VS2015), и я не пришел к такому же выводу:

public enum TestEnum : UInt32
{

}

и

public enum MyEnum : uint
{

}

оба полностью действительны. Почему это так? Или изменилось?


EDIT:

Итак, чтобы прояснить ситуацию, это было изменение в С# 6, которое еще не задокументировано, и оно будет задокументировано в ближайшее время, так как вы можете прочитать эту проблему git на Roslyn Github

Ответ 1

Конечно, странно, что теперь он работает с С# 6. В текущей спецификации выполнения по-прежнему отображается следующая грамматика для указания базы в перечислениях перечислений:

enum_base
    : ':' integral_type
    ;

И интегральные типы определяются как актуальные фиксированные токены:

integral_type
    : 'sbyte'
    | 'byte'
    | 'short'
    | 'ushort'
    | 'int'
    | 'uint'
    | 'long'
    | 'ulong'
    | 'char'
    ;

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

Учитывая, что это не то, что происходит, есть две возможности: либо парсер был изменен намеренно, чтобы принять там не сглаженные типы, либо парсер неправильно принял их.

Если мы посмотрим на реализацию Roslyn, тогда мы увидим, почему это требование в спецификации не применяется. Анализатор объявления enum просто не проверяет его, а анализирует любой тип:

BaseListSyntax baseList = null;
if (this.CurrentToken.Kind == SyntaxKind.ColonToken)
{
    var colon = this.EatToken(SyntaxKind.ColonToken);
    var type = this.ParseType(false);
    var tmpList = _pool.AllocateSeparated<BaseTypeSyntax>();
    tmpList.Add(_syntaxFactory.SimpleBaseType(type));
    baseList = _syntaxFactory.BaseList(colon, tmpList);
    _pool.Free(tmpList);
}

На данный момент это не сильно отличается от кода для "нормального" наследования. Поэтому любое ограничение типа не применяется на уровне синтаксиса, но на семантическом уровне - в котором псевдоним типа точки, вероятно, уже оценивается.

Так что это кажется ошибкой: либо в спецификации, либо в синтаксическом анализаторе. Учитывая, что спецификация все еще работает, это может быть исправлено позже.

Ответ 2

Либо это надзор в новом компиляторе, либо они приняли решение не иметь эту несогласованность (?) на языке больше.

В настоящий момент официальная спецификация не является официальной спецификацией, а не только официальным выглядящим репозиторием git, где выполняется работа:

https://github.com/ljw1004/csharpspec/blob/gh-pages/enums.md

В настоящий момент похоже, что не использовать псевдонимы для базового типа перечисления должно быть ошибкой. Язык выглядит так же, как и предыдущая официальная спецификация. Но поскольку как компилятор, так и спецификация, похоже, все еще находятся в процессе разработки, трудно сказать, какой из них правильный.

Ответ 3

Это было намеренное изменение языка в С# 6, спецификации которого были опубликованы только в черновом формате. Это было такое маленькое изменение, что мы не забываем вводить его в спецификации.

См. также https://github.com/dotnet/roslyn/issues/623