Что означает атрибут Enlue Enum в C #?

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

[Flags] public enum Options { None = 0, Option1 = 1, Option2 = 2, Option3 = 4, Option4 = 8 } 

Я не понимаю, что именно делает [Flags] -трибут.

У кого-нибудь есть хорошее объяснение или пример, который они могут опубликовать?

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

 myProperties.AllowedColors = MyColor.Red | MyColor.Green | MyColor.Blue; 

Обратите внимание, что [Flags] сам по себе этого не меняет – все, что он делает, это включить хорошее представление методом .ToString() :

 enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 } [Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 } ... var str1 = (Suits.Spades | Suits.Diamonds).ToString(); // "5" var str2 = (SuitsFlags.Spades | SuitsFlags.Diamonds).ToString(); // "Spades, Diamonds" 

Также важно отметить, что [Flags] автоматически не делают значения enums равными двум. Если вы опустите числовые значения, перечисление не будет работать так, как можно было бы ожидать в побитовых операциях, потому что по умолчанию значения начинаются с 0 и приращения.

Неверное объявление:

 [Flags] public enum MyColors { Yellow, Green, Red, Blue } 

Значения, если объявлено таким образом, будут желтыми = 0, зеленым = 1, красным = 2, синим = 3. Это сделает его бесполезным для использования в качестве флагов.

Вот пример правильного объявления:

 [Flags] public enum MyColors { Yellow = 1, Green = 2, Red = 4, Blue = 8 } 

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

 if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow) { // Yellow has been set... } if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green) { // Green has been set... } 

или, в .NET 4 и более поздних версиях:

 if (myProperties.AllowedColors.HasFlag(MyColor.Yellow)) { // Yellow has been set... } 

Под крышками

Это работает, потому что вы ранее использовали полномочия двух в вашем перечислении. Под обложками ваши значения enums выглядят так (представлены как байты, которые имеют 8 бит, которые могут быть 1 или 0)

  Yellow: 00000001 Green: 00000010 Red: 00000100 Blue: 00001000 

Аналогично, после того, как вы установили свойство AllowedColors в красный, зеленый и синий (какие значения имеют OR’ed по каналу |), AllowedColors выглядит так

 myProperties.AllowedColors: 00001110 

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

 myProperties.AllowedColors: 00001110 MyColor.Green: 00000010 ----------------------- 00000010 // Hey, this is the same as MyColor.Green! 

Значение None = 0

И относительно использования 0 в вашем перечислении, цитируя из msdn:

 [Flags] public enum MyColors { None = 0, .... } 

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

Вы можете найти дополнительную информацию об атрибуте flags и его использовании в msdn и создании флагов в msdn

Вы также можете сделать это

 [Flags] public enum MyEnum { None = 0, First = 1 << 0, Second = 1 << 1, Third = 1 << 2, Fourth = 1 << 3 } 

Я считаю, что бит-сдвиг проще, чем набирать 4,8,16,32 и так далее. Это не влияет на ваш код, потому что все это делается во время компиляции

Объединяя ответы https://stackoverflow.com/a/8462/1037948 (декларация с помощью бит-сдвига) и https://stackoverflow.com/a/9117/1037948 (используя комбинации в декларации), вы можете битовать предыдущие значения чем использование чисел. Не обязательно рекомендовать это, но просто указывая на то, что вы можете.

Скорее, чем:

 [Flags] public enum Options : byte { None = 0, One = 1 << 0, // 1 Two = 1 << 1, // 2 Three = 1 << 2, // 4 Four = 1 << 3, // 8 // combinations OneAndTwo = One | Two, OneTwoAndThree = One | Two | Three, } 

Вы можете объявить

 [Flags] public enum Options : byte { None = 0, One = 1 << 0, // 1 // now that value 1 is available, start shifting from there Two = One << 1, // 2 Three = Two << 1, // 4 Four = Three << 1, // 8 // same combinations OneAndTwo = One | Two, OneTwoAndThree = One | Two | Three, } 

Подтверждение с помощью LinqPad:

 foreach(var e in Enum.GetValues(typeof(Options))) { string.Format("{0} = {1}", e.ToString(), (byte)e).Dump(); } 

Результаты в:

 None = 0 One = 1 Two = 2 OneAndTwo = 3 Three = 4 OneTwoAndThree = 7 Four = 8 

Ниже приведен пример, который показывает декларацию и потенциальное использование:

 namespace Flags { class Program { [Flags] public enum MyFlags : short { Foo = 0x1, Bar = 0x2, Baz = 0x4 } static void Main(string[] args) { MyFlags fooBar = MyFlags.Foo | MyFlags.Bar; if ((fooBar & MyFlags.Foo) == MyFlags.Foo) { Console.WriteLine("Item has Foo flag set"); } } } } 

Недавно я спросил о чем-то подобном.

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

Это позволяет:

 [Flags] public enum PossibleOptions : byte { None = 0, OptionOne = 1, OptionTwo = 2, OptionThree = 4, OptionFour = 8, //combinations can be in the enum too OptionOneAndTwo = OptionOne | OptionTwo, OptionOneTwoAndThree = OptionOne | OptionTwo | OptionThree, ... } 

Тогда вы можете сделать:

 PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree if( opt.IsSet( PossibleOptions.OptionOne ) ) { //optionOne is one of those set } 

Я считаю, что это легче читать, чем большинство способов проверки включенных флагов.

@Nidonocu

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

 Mode = Mode.Read; //Add Mode.Write Mode |= Mode.Write; Assert.True(((Mode & Mode.Write) == Mode.Write) && ((Mode & Mode.Read) == Mode.Read))); 

В дополнение к принятому ответу, в C # 7 флаги enums могут быть записаны с использованием двоичных литералов:

 [Flags] public enum MyColors { None = 0b0000, Yellow = 0b0001, Green = 0b0010, Red = 0b0100, Blue = 0b1000 } 

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

Чтобы добавить Mode.Write :

 Mode = Mode | Mode.Write; 

Есть что-то слишком многословное для конструкции if ((x & y) == y)... , особенно если x AND y – это как совокупные наборы флагов, так и вы хотите знать только, есть ли какие- либо совпадения.

В этом случае вам действительно нужно знать, есть ли ненулевое значение [1] после того, как вы набрали битмаски .

[1] См. Комментарий Хайме. Если бы мы были достоверно битовой масками , нам нужно было бы только проверить, что результат положительный. Но поскольку enum s может быть отрицательным, даже, как ни странно, в сочетании с атрибутом [Flags] , это защитный код для != 0 а не > 0 .

Построение настроек @ andnil …

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BitFlagPlay { class Program { [Flags] public enum MyColor { Yellow = 0x01, Green = 0x02, Red = 0x04, Blue = 0x08 } static void Main(string[] args) { var myColor = MyColor.Yellow | MyColor.Blue; var acceptableColors = MyColor.Yellow | MyColor.Red; Console.WriteLine((myColor & MyColor.Blue) != 0); // True Console.WriteLine((myColor & MyColor.Red) != 0); // False Console.WriteLine((myColor & acceptableColors) != 0); // True // ... though only Yellow is shared. Console.WriteLine((myColor & MyColor.Green) != 0); // Wait a minute... ;^D Console.Read(); } } } 

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

 [Flags] public enum DashboardItemPresentationProperties : long { None = 0, HideCollapse = 1, HideDelete = 2, HideEdit = 4, HideOpenInNewWindow = 8, HideResetSource = 16, HideMenu = 32 } 
Давайте будем гением компьютера.