Должен ли Enum начинаться с 0 или 1?


136

Представьте, что я определил следующее Enum:

public enum Status : byte
{
    Inactive = 1,
    Active = 2,
}

Как лучше всего использовать enum? Должно ли оно начинаться с 1примера, приведенного выше, или начинаться с 0(без явных значений) следующим образом:

public enum Status : byte
{
    Inactive,
    Active
}

13
Вам действительно нужно их нумеровать явно?
Yuck

164
Перечисления были созданы только для того, чтобы такие вещи не были важны.
BoltClock

9
@ Даниэль - нет! Лучше использовать перечисление, когда вы думаете, что логическое значение подойдет, чем использовать логическое, когда вы думаете о перечислении.
AAT

22
@Daniel из-за значения
FileNotFound

5
xkcd.com/163 применяется к enum даже лучше, чем к массивам индексов.
оставлено около

Ответы:


161

Руководство по проектированию рамок :

✔️ НЕОБХОДИМО указывать нулевое значение для простых перечислений.

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

Руководство по проектированию рамок / Разработка перечислений флагов :

❌ ИЗБЕГАЙТЕ, используя значения перечисления флага, равные нулю, если только это значение не представляет «все флаги очищены» и не названо соответствующим образом, как предписано в следующем руководстве.

✔️ НУЖНО назвать нулевое значение перечислений флагов Нет. Для перечисления флагов значение всегда должно означать «все флаги очищены».


28
Ошибка рано: если «None» не подходит, но нет логического значения по умолчанию, я бы все равно поставил нулевое значение (и назову его «None» или «Invalid»), которое не предназначено для использования, просто чтобы если член класса этого перечисления не инициализирован должным образом, неинициализированное значение может быть легко обнаружено, и в switchинструкциях оно перейдет к defaultразделу, где я выбрасываю a InvalidEnumArgumentException. В противном случае программа может непреднамеренно продолжить работу с нулевым значением перечисления, которое может быть допустимым и остаться незамеченным.
Аллон Гуралнек

1
@Allon Кажется, лучше указывать только допустимые значения перечисления, а затем проверять наличие недопустимых значений в установщике и / или конструкторе. Таким образом, вы сразу узнаете, что какой-то код работает неправильно, вместо того, чтобы позволить объекту с недопустимыми данными существовать в течение некоторого неизвестного времени и узнать об этом позже. Если «Нет» представляет допустимое состояние, вы не должны его использовать.
апреля

@SoloBold: Это похоже на случай, когда вы не забыли инициализировать член класса enum в конструкторе. Никакая проверка не поможет вам, если вы забудете инициализировать или забудете подтвердить. Кроме того, существуют простые классы DTO, которые не имеют конструкторов, но вместо этого полагаются на инициализаторы объектов . Выискивание такого жука может быть чрезвычайно болезненным. Тем не менее, добавление неиспользуемого значения перечисления создает уродливый API. Я бы избежал этого для API, предназначенных для общественного потребления.
Аллон Гуралнек

@Allon Это хороший момент, но я бы сказал, что вы должны проверять перечисления в функции-установщике и выбрасывать из метода-получателя, если он никогда не вызывался. Таким образом, у вас есть одна точка отказа, и вы можете планировать на поле всегда правильное значение, что упрощает дизайн и код. Мои два цента в любом случае.
апреля

@SoloBold: представьте себе класс DTO с 15 автоматически реализуемыми свойствами. Его тело длиной 15 строк. Теперь представьте тот же класс с обычными свойствами. Это минимум 180 строк перед добавлением логики проверки. Этот класс используется исключительно для внутренних целей передачи данных. Какой из них вы бы предпочли сохранить, класс из 15 строк или класс из 180+ строк? Краткость имеет свою ценность. Но все же оба наших стиля верны, они просто разные. (Я предполагаю, что именно здесь AOP вовлекается и выигрывает обе стороны аргумента).
Аллон Гуралнек

66

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


9
+1, согласен, но только в том случае, если ваш код использует целое число по какой-то внешней причине (например, сериализация). В любом другом месте вы должны придерживаться того, чтобы фреймворк выполнял свою работу. Если вы полагаетесь на целочисленные значения внутри, то вы, вероятно, делаете что-то не так (см .: пусть фреймворк выполняет свою работу).
Мэтью Шарли

3
Мне нравится сохранять текст перечисления. Делает базу данных гораздо более полезной IMO.
Дейв

2
Обычно вам не нужно явно устанавливать значения ... даже когда они сериализуются. просто ВСЕГДА добавляйте новые значения в конце. это решит проблемы сериализации. в противном случае вам может потребоваться управление версиями вашего хранилища данных (например, заголовок файла, содержащий версию для изменения поведения при чтении / записи значений) (или см. шаблон
памяти

4
@Dave: Если вы не объясните документ, что перечисленные тексты являются священными, вы настроите себя на неудачу, если будущий программист решит изменить имя, чтобы оно было более понятным или соответствовало некоторому соглашению об именах.
суперкат

1
@pstrjds: это стилистический компромисс, я полагаю - дисковое пространство дешево, хотя время, потраченное на постоянное преобразование между значениями enum и intw при поиске в БД, относительно дорого (вдвойне, если у вас есть настроенный инструмент отчетности для базы данных или чего-то подобного) , Если вы беспокоитесь о свободном пространстве, с более новыми версиями SQL-сервера вы можете сжать базу данных, что означает, что 1000 экземпляров SomeEnumTextualValue будут занимать чуть больше места. Конечно, это не будет работать для всех проектов - это компромисс. Я думаю, что беспокойство о пропускной способности пахнет преждевременной оптимизацией, возможно!
Дэйв

15

Enum является типом значения, и его значение по умолчанию (например, для поля Enum в классе) будет 0, если не инициализировано явно.

Поэтому вы обычно хотите иметь 0 в качестве определенной константы (например, Неизвестно).

В вашем примере, если вы хотите Inactiveбыть значением по умолчанию, тогда оно должно иметь значение ноль. В противном случае вы можете рассмотреть возможность добавления константы Unknown.

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

  • Флаги перечислений

  • Перечисления, значения которых используются во взаимодействии с внешними системами (например, COM).


Я обнаружил, что перечисление флагов намного удобнее для чтения, когда вы не устанавливаете значения явно. - Также гораздо меньше ошибок, позволяющих компилятору делать двоичную математику за вас. (то [Flags] enum MyFlags { None = 0, A, B, Both = A | B, /* etc. */ }есть более читабельна, чем [Flags] enum MyFlags { None = 0, A = 1, B = 2, Both = 3, /* etc */ }.)
BrainSlugs83

1
@ BrainSlugs83 - я не вижу, как это было бы полезно в общем случае - например, [Flags] enum MyFlags { None=0, A, B, C } привело бы к [Flags] enum MyFlags { None=0, A=1, B=2, C=3 }, тогда как для перечисления Flags вы обычно хотели бы C = 4.
Джо

14

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

public enum Status : byte
{
    Inactive,
    Active
}

6

Я бы сказал, что лучшая практика - не нумеровать их и позволять им быть неявными - что начиналось бы с 0. Поскольку это неявное, это языковые предпочтения, которым всегда хорошо следовать :)


6

Я бы начал перечисление логического типа с 0.

Если только «Inative» означает нечто иное, чем «Inactive» :)

Это сохраняет стандарт для тех.


6

Я бы сказал, это зависит от того, как вы их используете. Для пометки enum рекомендуется иметь Noneзначение 0 , например:

[Flags]
enum MyEnum
{
    None = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    All = Option1 | Option2 | Option3,
}

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

В других случаях я бы оставил все как есть, не заботясь о том, начинаются ли они с 0 или 1.


5

Если у вас нет веской причины для использования необработанных значений, вам следует использовать только неявные значения и ссылаться на них с помощью Status.Activeи Status.Inactive.

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

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

Если вы планируете использовать Enum в качестве набора флагов, есть простое соглашение, которое стоит придерживаться:

enum Example
{
  None      = 0,            //  0
  Alpha     = 1 << 0,       //  1
  Beta      = 1 << 1,       //  2
  Gamma     = 1 << 2,       //  4
  Delta     = 1 << 3,       //  8
  Epsilon   = 1 << 4,       // 16
  All       = ~0,           // -1
  AlphaBeta = Alpha | Beta, //  3
}

Значения должны быть степенью двойки и могут быть выражены с использованием операций сдвига битов. None, очевидно, должно быть 0, но Allменее очевидно -1. ~0является двоичным отрицанием 0и приводит к числу, для которого установлен каждый бит 1, который представляет значение-1 . Для составных флагов (часто используемых для удобства) другие значения могут быть объединены с использованием побитового или оператора |.


3

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


3

Если не указано, нумерация начинается с 0.

Важно быть явным, поскольку перечисления часто сериализуются и хранятся как int, а не как строка.

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

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

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

public enum Status : byte
{
    Inactive = 1,
    Active
}

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


2

Если вы начнете с 1, то вы можете легко получить количество ваших вещей.

{
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

Если вы начинаете с 0, то используйте первый как значение для неинициализированных вещей.

{
    BOX_NO_THING   = 0,
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

5
Прости, Джонатан. Я думаю, это предложение - немного "старая школа" в моем уме (вид соглашения прибывает из низкоуровневых лет). Это нормально как быстрое решение для "встраивания" некоторой дополнительной информации о перечислении, но это не очень хорошая практика в больших системах. Вы не должны использовать enum, если вам нужна информация о количестве доступных значений и т. Д. А как насчет BOX_NO_THING1? Вы бы дали ему BOX_NO_THING + 1? Перечисления должны использоваться как то, для чего они предназначены: Специфические (int) значения, представленные «говорящими» именами.
Beachwalker

Гектометр Вы предполагаете, что это старая школа, потому что я использовал все заглавные буквы, а не MicrosoftBumpyCaseWithLongNames. Хотя я согласен, что лучше использовать итераторы, чем цикл, пока не достигнете перечисленного определения XyzNumDefsInMyEnum.
Джонатан Клайн IEEE

Это ужасная практика в C # разными способами. Теперь, когда вы идете, чтобы получить количество ваших перечислений в правильном порядке, или если вы пытаетесь перечислить их в правильном порядке, вы получите дополнительный, дублирующий объект. Кроме того, это делает вызов .ToString () потенциально неоднозначным (испортить современную сериализацию), среди прочего.
BrainSlugs83

0

Прежде всего, если вы не указываете конкретные значения по какой-либо причине (числовое значение имеет значение где-то еще, например, База данных или внешняя служба), тогда вообще не указывайте числовые значения и не допускайте их явного указания.

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


0

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

Вы enumдолжны начать именно там, где это необходимо. Это также не должно быть последовательным. Значения, если они установлены явно, должны отражать некоторое семантическое значение или практическое рассмотрение. Например, enum«бутылки на стене» должны быть пронумерованы от 1 до 99, в то время как enumдля степеней 4, вероятно, они должны начинаться с 4 и продолжаться с 16, 64, 256 и т. Д.

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


-1

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


4
Ужасная идея Как тип значения, перечисления всегда инициализируются нулями. Если у вас будет какое-то значение, представляющее неизвестное или неинициализированное значение, оно должно быть 0. Вы не можете изменить значение по умолчанию на -1, заполнение нулями жестко запрограммировано в CLR.
Бен Фойгт

Ах, я этого не осознавал Я обычно устанавливаю значение enum-значения / свойства, когда делаю / инициализирую. Спасибо за указатель.
Томас МакГиннесс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.