Почему приведение типа enum разрешено, даже если в перечислении не определено допустимое значение - PullRequest
4 голосов
/ 11 июля 2011

При чтении ответа Джона Скита на этот конкретный вопрос Как я могу сделать так, чтобы мои переменные класса могли быть установлены только на один из трех вариантов выбора? вы узнали что-то новое, яя не знал об этом в C #, и я думаю, что система типов CLR.

В чем причина того, что это допустимо:

public enum Tests
{
    Zero = 0,
    One,
    Two
}

var nonExistantTestsValue = (Tests)1000;

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

Если ограничение, налагаемое определением enum, так легкосломан, какой смысл (кроме вопроса читабельности)?Очевидно, вы можете убедиться, что это допустимое значение с помощью отражения, но почему это не делается во время компиляции?

Вероятно, есть веская причина для того, чтобы это работало так, как оно работает, но я не вижу его.

Ответы [ 5 ]

5 голосов
/ 11 июля 2011

Перечисления - это по существу уникальные типы, которые позволяют назначать символические имена целым значениям.Они не используются для ограничения допустимых значений переменной ...

3 голосов
/ 11 июля 2011

Если ограничение, накладываемое определением перечисления, так легко нарушается, в чем смысл

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

Веские причины:

Пропустите первую пулю, если вы не хотите видеть простые истины прямо сейчас

  • спецификация языка [ причина, о которой я упоминаю, состоит в том, чтобы напомнить людям об ограниченном использовании обсуждаемых фактов; фраза типа ... then what's the point вызывает это для меня ]

  • производительность (трудно / невозможно сказать, когда валидация не нужна , и это сильно снизит производительность для конкретных приложений

(Помните, что функции CLR можно вызывать из любого места, а не только из C #, не только из вашей сборки)

2 голосов
/ 11 июля 2011

Больше сомнений в том, что вы можете делать, когда перечисления не ограничены значениями:

Флаги - один из следующих примеров:

[Flags]
enum MyFlag
{
     a,
     b,
     c
}

Теперь вы можете выполнять битовые операции:

MyFlag flags = MyFlag.a|MyFlag.b;
1 голос
/ 11 июля 2011

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

Давайте посмотрим на IL-представление перечисления:

.class private auto ansi sealed MyEnum
    extends [mscorlib]System.Enum
{
    // Fields
    .field public specialname rtspecialname int32 value__
    .field public static literal valuetype MyEnum Value0 = int32(0)
    .field public static literal valuetype MyEnum Value1 = int32(1)
    .field public static literal valuetype MyEnum Value2 = int32(2)

}

Сначала отметим, что это тип значения и, следовательно, (MyEnum)0 должно быть допустимым. Во-вторых, мы видим, что возможные значения перечислений равны всего const с, а перечисления на уровне времени выполнения совместимы по присваиванию с целочисленными литералами.

Константы перечисления обычно становятся целочисленными литералами. Поэтому, если вы хотите предотвратить появление недопустимых перечислений, вам потребуется ввести дорогостоящие проверки времени выполнения при преобразовании из перечисления или нетривиальные проверки времени загрузки кросс-сборки, чтобы убедиться, что литералы перечисления, выпекаемые в другой сборке, действительны.


Другое дело, что можно создавать перечисления, поддерживаемые long. Но одно свойство longs заключается в том, что их назначение не гарантируется как атомарное. Поэтому гарантировать, что значение перечисления на основе long действительно, трудно.

enum MyLongEnum:long
{
  Value1=0x0101010102020202,
  Value2=0x0303030304040404
}

Если вы присвоили такое перечисление из нескольких потоков, вы можете получить смешанное значение, которое недопустимо, даже если вы никогда не назначали недопустимое значение.


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

1 голос
/ 11 июля 2011

Допустимо любое значение, потому что вы можете пометить перечисление атрибутом "Flags".Это означает, что вы можете составить любое значение, ИЛИ-различные члены самого перечисления.По сути, компилятор недостаточно умен, чтобы позаботиться о любом возможном способе использования перечисления.

РЕДАКТИРОВАТЬ: нашел предыдущий пост Эрика Липперта:

Почему приведение int кневерное значение перечисления НЕ выдает исключение?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...