Почему компилятор C# разрешает пустые перечисления?


я случайно определил перечисление сегодня, которое не содержало никаких значений. Как этот, например:

public enum MyConfusingEnum{}

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

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

var mySadCompiler = MyConfusingEnum;

не указывает значение, но достаточно интересно I был может сказать,..

var myRoundTheHousesZeroState = Activator.CreateInstance<MyConfusingEnum>();

который, как я уже упоминал, является типом значения MyConfusingEnum со значением 0;

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

5 71

5 ответов:

во-первых, вы могли бы сделать это много легко:

MyConfusingEnum x1 = 0;
MyConfusingEnum x2 = default(MyConfusingEnum);
MyConfusingEnum x3 = new MyConfusingEnum();
MyConfusingEnum x4 = (MyConfusingEnum) 123;

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

мой вопрос в том, почему компилятор разрешает пустое определение

Я начну с ответа на ваш вопрос вопросом. У вас также есть компилятор отказать?

class C {}
interface I {}
struct S {}

почему нет?

чтобы более прямо не отвечать на ваш вопрос: "почему мир не отличается от него?"вопросы трудно ответить. Вместо ответа на этот невозможный вопрос я отвечу на вопрос: "предположим, что пустые перечисления были переданы команде разработчиков; как бы вы ответили на этот шаг?- Этот вопрос все еще контрфактическое но по крайней мере это я могу на него ответить.

вопрос тогда становится ли стоимостью функция оправдана его преимущества.

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

однако, это на самом деле не соответствующие расходы. Элемент стоимостью - это издержки. Бюджеты конечны, функции не свободны, и поэтому любая функция, которая реализована, означает, что какая-то другая функция должна быть сокращена; какую функцию C# вы хотели бы сократить, чтобы получить эту функцию? Элемент проиграл выгода от не возможность сделать лучшую функцию-это возможность стоимость.

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

есть ли какие-либо сценарии, где это может быть полезно?

никто не приходит на ум. "Отклонение программ, которые явно не полезны" не является целью дизайна C#.

вы можете привести любое значение базового типа integer (думаю int по умолчанию) в enum - так (MyConfusingEnum)42 теперь будет этого типа перечисления.

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

пример (предполагая, что код инкапсулирует некоторое "состояние на основе int" в Enum:

enum ExternalDeviceState {};

ExternalDeviceState GetState(){ ... return (ExternalDeviceState )intState;}
bool IsDeviceStillOk(ExternalDeviceState currentState) { .... }

спецификация действительно позволяет пустое перечисление:

14.1 перечисление заявления

объявление перечисления объявляет новый тип перечисления. Объявление перечисления начинается с ключевого слова enum и определяет имя, доступность, базовый тип, и членов перечисления.

enum-declaration:
   attributesopt   enum-modifiersopt   enum   identifier
        enum-base(opt)   enum-body   ;(opt)

enum-base:
:   integral-type

enum-body:
  {   enum-member-declarations(opt)   }  
  {   enum-member-declarations   ,   }

отметим, что enum-member-declarations(opt) явно помечается как вариант, где внутри ничего нет {}.

Activator.CreateInstance<MyConfusingEnum>(); Это то же самое, что new MyConfusingEnum(). ( docs)

вызов конструктора перечисления дает вам 0 как ценность.

из-за проектного решения перечисление может иметь любое значение, допустимое для типа поддержки (обычно int), это не должно быть значение, определенное в перечислении.

по причине этого проектного решения, я могу указать вам на ответ на вопрос под названием " почему приведение int к недействительному значение enum не выбрасывает исключение?"

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

существуют ли какие-либо сценарии, где это может быть полезно?

как уже упоминалось, вы можете присвоить этому перечислению любое значение, которое базовый тип разрешает простым приведением. Таким образом, Вы можете применять проверку типов в ситуациях, когда int может привести к путанице. Например:

public enum Argb : int {}

public void SetColor(Argb a) { ....

или вы хотите иметь некоторые методы расширения, не загромождая тип данных int, с ними

public static Color GetColor(this Argb value) {
    return new Color( (int)value );
}

public static void Deconstruct( this Argb color, out byte alpha, out byte red, out byte green, out byte blue ) {
        alpha = (byte)( (uint)color >> 24 );
        red = (byte)( (uint)color >> 16 );
        green = (byte)( (uint)color >> 8 );
        blue = (byte)color;
}

и использовать его как

var (alpha, red, green, blue) = color;

есть ли какие-либо сценарии, где это может быть полезно?

Я испытал один в мире Java.

в случае перечисления вы знаете все возможные значения во время компиляции.

однако, есть время до времени компиляции, в котором вы можете не знать все значения (пока) или в котором вы просто не собираетесь реализовывать какие-либо значения, пока.

в в ходе разработки API я реализовал перечисление без каких-либо значений, чтобы позволить ссылаться на него из других интерфейсов. Я добавил значения позже.