.NET: Почему диапазон / значение Enum не проверяются? - PullRequest
8 голосов
/ 11 января 2009

Это всегда беспокоило меня. Возможно, кто-то, обладающий глубокими познаниями в .NET, сможет мне это объяснить.

Предположим, я определил перечисление следующим образом:

public enum Foo
{
   Eenie = 1,
   Meenie = 2,
   Miney = 3,
   Moe = 4
}

Теперь также предположим, что где-то в моем коде у меня есть следующий код:

int bar = (Foo)5;

Это скомпилируется просто отлично, и никакое исключение не будет выдано, даже если значение 5 явно не является допустимым значением, определенным в Foo.

Или рассмотрим следующее:

public void ProcessFoo(Foo theFoo)
{
    // Do processing
}

public static void Main()
{
    ProcessFoo((Foo)5);
}

Опять не исключение.

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

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

Итак, опять же, какая непреодолимая причина, которая могла бы привести к решению не иметь проверенного перечисления?

Для справки, с документация MSDN для класса Enum :

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

Ответы [ 9 ]

14 голосов
/ 11 января 2009

Проблема была в производительности. Достаточно просто иметь проверенное перечисление для нормальных перечислений, таких как Color

enum Color {
  Red,
  Blue
}

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

enum Property {
  IsFirst = 0x1,
  IsDefault = 0x2,
  IsLastAccessed = 0x4
}

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

4 голосов
/ 11 января 2009

Проверка диапазона имеет потенциально ненужные затраты. Поэтому разумно не выполнять это неявно. Как уже упоминалось, [Flags] требует, чтобы такая проверка не проводилась. Если среда выполнения проверит наличие [Flags], это все равно повлечет штраф за время выполнения при каждом выполнении преобразования.

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

2 голосов
/ 11 января 2009

Если они проверяются по диапазону, как бы вы получили [Flags] перечисления и скомбинировали их, используя поразрядно или?

Примером будет ControlStyles enum

1 голос
/ 11 января 2009

Я вижу две причины:

  1. [Флаги] работают плавно, без проверок. Для вашего примера

    (Foo) 5 == Foo.Eenie | Foo.Moe;

  2. Enum является типом значения. Если вы не инициализируете его, он будет равен нулю. Если вы хотите проверить значения перечисления, непонятно, когда в этом случае следует генерировать исключение - нулевое значение может проникнуть в ваш код, когда вы, например, создаете экземпляр класса, содержащий это перечисление в качестве поля.

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

Более того, вы всегда должны явно выполнять свои проверки и генерировать исключения для значений, которые вы не можете обработать. В противном случае добавление новых значений в ваше перечисление может изменить поведение вашего существующего кода. К счастью, если вы используете единственный оператор switch, это метод, который возвращает значение, компилятор заставит вас явно указать, что вы хотите сделать, если совпадений не найдено - в разделе по умолчанию для switch после него вам придется возвращать значение или выдавать исключение. , NotSupported исключение предпочтения в большинстве случаев.

1 голос
/ 11 января 2009

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

Еще один убедительный:

enum VeryHardToRangeCheck {
  one = 1,
  three = 3,
  five = 5
}
0 голосов
/ 16 декабря 2009

Извините, некро. Возможно ли, что вы путаете Enum с коллекцией ключ-значение?

Когда вы присваиваете целое число Meenie = 2 элементу enum, все, что вы делаете, это говорите, что Meenie является третьим индексом, и все, что после него, если не указано, будет иметь индекс 2+ (расстояние от Meenie ). Поэтому, когда вы ищете Foo [5], вы ищете индекс 5, а не какой-то ключ, значение которого равно 5.

В большинстве случаев вы бы этого не сделали; вы бы попросили Foo.Meenie - это точка перечисления, чтобы установить известный диапазон значений, а затем обращаться к ним по их открытым именам. Это просто удобство разработчика. Есть лучшие структуры для того, чтобы делать то, что вы делаете в вашем примере.

0 голосов
/ 11 апреля 2009

Microsoft C # Руководство по программированию специально говорит не делать то, о чем вы просите:

Можно назначить любое произвольное целочисленное значение для meetingDay. Например, эта строка кода не выдает ошибку: meetingDay = (Days) 42. Однако не следует этого делать, поскольку неявное ожидание состоит в том, что переменная enum будет содержать только одно из значений, определенных перечислением. Присвоение произвольного значения переменной типа перечисления означает высокий риск ошибок.

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

enum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

Тип хранилища определяет, сколько памяти использует перечисление.

0 голосов
/ 11 января 2009

Я бы сказал, что причина в том, что перечисления проверяются только типом и вставляются компилятором во время компиляции. Особенно полезно для расширения перечислений с атрибутом Flags (поскольку он внезапно становится совместимым с прямой связью ...

0 голосов
/ 11 января 2009

Это потому, что (Foo)5 равно Foo.Eenie | Foo.Moe

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