Почему у разрешений enum часто есть значения 0, 1, 2, 4?

Почему люди всегда используют значения enum, такие как 0, 1, 2, 4, 8, а не 0, 1, 2, 3, 4?

Это что-то связано с битовыми операциями и т.д.?

Я бы очень признателен за небольшой фрагмент образца о том, как это правильно использовать:)

[Flags]
public enum Permissions
{
    None   = 0,
    Read   = 1,
    Write  = 2,
    Delete = 4
}

Ответ 1

Потому что они имеют силу два, и я могу это сделать:

var permissions = Permissions.Read | Permissions.Write;

И возможно позже...

if( (permissions & Permissions.Write) == Permissions.Write )
{
    // we have write access
}

Это бит-поле, где каждый бит набора соответствует некоторому разрешению (или тому, что логически соответствует перечисленному значению). Если они были определены как 1, 2, 3, ..., вы не сможете использовать побитовые операторы таким образом и получать значимые результаты. Чтобы углубиться в глубину...

Permissions.Read   == 1 == 00000001
Permissions.Write  == 2 == 00000010
Permissions.Delete == 4 == 00000100

Обратите внимание на шаблон здесь? Теперь, если взять оригинальный пример, т.е.

var permissions = Permissions.Read | Permissions.Write;

Тогда...

permissions == 00000011

См? Оба бита Read и Write установлены, и я могу проверить это независимо (также обратите внимание, что бит Delete не установлен, и поэтому это значение не передает разрешения на удаление).

Он позволяет хранить несколько флагов в одном поле бит.

Ответ 2

Если из других ответов все еще не ясно, подумайте об этом так:

[Flags] 
public enum Permissions 
{   
   None = 0,   
   Read = 1,     
   Write = 2,   
   Delete = 4 
} 

- это всего лишь более короткий способ написать:

public enum Permissions 
{   
    DeleteNoWriteNoReadNo = 0,   // None
    DeleteNoWriteNoReadYes = 1,  // Read
    DeleteNoWriteYesReadNo = 2,  // Write
    DeleteNoWriteYesReadYes = 3, // Read + Write
    DeleteYesWriteNoReadNo = 4,   // Delete
    DeleteYesWriteNoReadYes = 5,  // Read + Delete
    DeleteYesWriteYesReadNo = 6,  // Write + Delete
    DeleteYesWriteYesReadYes = 7, // Read + Write + Delete
} 

Существует восемь возможностей, но вы можете представлять их как комбинации только четырех членов. Если бы было шестнадцать возможностей, вы могли бы представлять их как комбинации только пяти членов. Если бы было четыре миллиарда возможностей, вы могли бы представить их в виде комбинаций из 33 членов! Очевидно, что гораздо лучше иметь только 33 члена, каждый (кроме нуля), равный двум, чем пытаться назвать четыре миллиарда элементов в перечислении.

Ответ 3

Поскольку эти значения представляют собой уникальные местоположения бит в двоичном формате:

1 == binary 00000001
2 == binary 00000010
4 == binary 00000100

и т.д., поэтому

1 | 2 == binary 00000011

EDIT:

3 == binary 00000011

3 в двоичном выражении представлено значением 1 как в одном месте, так и в двухместном месте. Это фактически то же самое, что и значение 1 | 2. Поэтому, когда вы пытаетесь использовать бинарные места в качестве флагов для представления некоторого состояния, 3 обычно не имеет смысла (если не существует логического значения, которое фактически является комбинацией двух)

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

[Flags]
public Enum Permissions
{
  None = 0,   // Binary 0000000
  Read = 1,   // Binary 0000001
  Write = 2,  // Binary 0000010
  Delete = 4, // Binary 0000100
  All = 7,    // Binary 0000111
}

Поэтому в я Permissions.All я также неявно имеет Permissions.Read, Permissions.Write и Permissions.Delete

Ответ 4

[Flags]
public Enum Permissions
{
    None   =    0; //0000000
    Read   =    1; //0000001
    Write  = 1<<1; //0000010
    Delete = 1<<2; //0000100
    Blah1  = 1<<3; //0001000
    Blah2  = 1<<4; //0010000
}

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

Ответ 5

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

[Flags]
public Enum Permissions
{
  None =  0x00,
  Read =  0x01,
  Write = 0x02,
  Delete= 0x04,
  Blah1 = 0x08,
  Blah2 = 0x10
}

Ответ 6

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

[Flags]
public enum FlagTest
{
    None = 0,
    Read = 1,
    Write = Read * 2,
    Delete = Write * 2,
    ReadWrite = Read|Write
}

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

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

Ответ 7

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

typedef NS_OPTIONS(NSUInteger, Align) {
    AlignLeft         = 00000001,
    AlignRight        = 00000010,
    AlignTop          = 00000100,
    AlignBottom       = 00001000,
    AlignTopLeft      = 00000101,
    AlignTopRight     = 00000110,
    AlignBottomLeft   = 00001001,
    AlignBottomRight  = 00001010
};

NSLog(@"%ld == %ld", AlignLeft | AlignBottom, AlignBottomLeft);

LOG 513 == 513

Так намного легче (для меня, по крайней мере) понять. Выровняйте те... описывайте желаемый результат, получите результат, который вы ХОТИТЕ.. Никаких "расчетов" не требуется.