O que o atributo Enum significa em C #?

De vez em quando eu vejo um enum como o seguinte:

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

Eu não entendo exatamente o que o atributo [Flags] faz.

Alguém tem uma boa explicação ou exemplo que poderia postar?

    O atributo flags deve ser usado sempre que o enumerável representar uma coleção de sinalizadores, em vez de um único valor. Tais collections são geralmente manipuladas usando operadores bit a bit, por exemplo:

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

    Note que [Flags] por si só não altera isto – tudo o que faz é habilitar uma boa representação pelo método .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" 

    Também é importante observar que [Flags] não faz automaticamente os poderes de valores enum de dois. Se você omitir os valores numéricos, o enum não funcionará como se poderia esperar em operações bit a bit, porque, por padrão, os valores começam com 0 e incrementam.

    Declaração incorreta:

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

    Os valores, se declarados desta forma, serão Amarelo = 0, Verde = 1, Vermelho = 2, Azul = 3. Isso tornará inútil para uso como sinalizadores.

    Aqui está um exemplo de uma declaração correta:

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

    Para recuperar os valores distintos em sua propriedade, pode-se fazer isso:

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

    ou, no .NET 4 e posterior:

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

    Debaixo das cobertas

    Isso funciona porque você usou anteriormente poderes de dois em sua enumeração. Nos bastidores, seus valores de enumeração se parecem com isso (apresentados como bytes, que tem 8 bits que podem ser 1 ou 0)

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

    Da mesma forma, depois de definir sua propriedade AllowedColors para Vermelho, Verde e Azul (que valores onde OR’ed by the pipe |), AllowedColors se parece com isso

     myProperties.AllowedColors: 00001110 

    Então, quando você recupera o valor, você é realmente bit a bit AND’ing os valores

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

    O valor None = 0

    E quanto ao uso 0 na sua enumeração, citando o msdn:

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

    Use None como o nome da constante enumerada da flag cujo valor é zero. Você não pode usar a constante enumerada None em uma operação AND bit a bit para testar um sinalizador porque o resultado é sempre zero. No entanto, você pode executar uma comparação lógica, não um bit a bit, entre o valor numérico ea constante enumerada None para determinar se os bits no valor numérico estão definidos.

    Você pode encontrar mais informações sobre o atributo flags e seu uso em msdn e projetar sinalizadores no msdn

    Você também pode fazer isso

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

    Acho que o bit-shifting é mais fácil do que digitar 4,8,16,32 e assim por diante. Não tem impacto no seu código porque tudo é feito em tempo de compilation

    Combinando respostas https://stackoverflow.com/a/8462/1037948 (declaração via mudança de bits) e https://stackoverflow.com/a/9117/1037948 (usando combinações na declaração) você pode alterar os valores anteriores do que usando números. Não necessariamente recomendando, mas apenas apontando você pode.

    Ao invés de:

     [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, } 

    Você pode declarar

     [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, } 

    Confirmando com o LinqPad:

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

    Resulta em:

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

    Por favor, veja o seguinte para um exemplo que mostra a declaração e o uso potencial:

     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"); } } } } 

    Eu perguntei recentemente sobre algo semelhante.

    Se você usar sinalizadores, poderá adicionar um método de extensão às enums para facilitar a verificação dos sinalizadores contidos (consulte a postagem para detalhes).

    Isso permite que você faça:

     [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, ... } 

    Então você pode fazer:

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

    Acho isso mais fácil de ler do que a maioria das maneiras de verificar os sinalizadores incluídos.

    @Nidonocu

    Para adicionar outro sinalizador a um conjunto existente de valores, use o operador de atribuição OU.

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

    Em extensão para a resposta aceita, em C # 7 os sinalizadores enum podem ser escritos usando literais binários:

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

    Acho que essa representação deixa claro como as bandeiras funcionam embaixo das capas .

    Para adicionar Mode.Write :

     Mode = Mode | Mode.Write; 

    Há algo excessivamente detalhado sobre o if ((x & y) == y)... construct, especialmente se x AND y são ambos conjuntos compostos de flags e você só quer saber se existe alguma sobreposição.

    Neste caso, tudo o que você realmente precisa saber é se existe um valor diferente de zero [1] depois de ter batizado com máscara .

    [1] Veja o comentário de Jaime. Se fôssemos autenticamente bitmasking , precisaríamos apenas verificar se o resultado foi positivo. Mas como enum s pode ser negativo, até mesmo, estranhamente, quando combinado com o atributo [Flags] , é defensivo codificar para != 0 vez de > 0 .

    Construindo fora da configuração do @ 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(); } } } 

    Sinalizadores permitem que você use bitmasking dentro de sua enumeração. Isso permite combinar valores de enumeração, enquanto retém quais são especificados.

     [Flags] public enum DashboardItemPresentationProperties : long { None = 0, HideCollapse = 1, HideDelete = 2, HideEdit = 4, HideOpenInNewWindow = 8, HideResetSource = 16, HideMenu = 32 }