Почему у разрешений 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
Так намного легче (для меня, по крайней мере) понять. Выровняйте те... описывайте желаемый результат, получите результат, который вы ХОТИТЕ.. Никаких "расчетов" не требуется.