Pregunta ¿Qué significa [Enlarge] Enum Attribute en C #?


De vez en cuando veo una enumeración como la siguiente:

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

No entiendo exactamente qué [Flags]-tributo hace.

¿Alguien tiene una buena explicación o ejemplo que podrían publicar?


1123
2017-08-12 04:09


origen


Respuestas:


El atributo de banderas se debe usar siempre que el enumerable represente una colección de banderas, en lugar de un único valor. Tales colecciones generalmente se manipulan utilizando operadores bit a bit, por ejemplo:

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

Tenga en cuenta que [Flags] por sí mismo no cambia esto en absoluto - Todo lo que hace es habilitar una buena representación por parte del .ToString() método:

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"

También es importante tener en cuenta que [Flags]  no hacer automáticamente que el enum calcule potencias de dos. Si omite los valores numéricos, la enumeración no funcionará como cabría esperar en las operaciones en modo bit, porque de manera predeterminada los valores comienzan con 0 e incrementan.

Declaración incorrecta:

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

Los valores, si se declaran de esta manera, serán Amarillo = 0, Verde = 1, Rojo = 2, Azul = 3. Esto lo hará inútil para usar como banderas.

Aquí hay un ejemplo de una declaración correcta:

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

Para recuperar los distintos valores en su propiedad, uno puede hacer esto:

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

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

o, en .NET 4 y posterior:

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

Debajo de las sábanas

Esto funciona porque anteriormente usaste poderes de dos en tu enumeración. Debajo de las carátulas, sus valores de enumeración se ven así (presentados como bytes, que tiene 8 bits que pueden ser 1 o 0)

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

Del mismo modo, después de haber establecido su propiedad AllowedColors a rojo, verde y azul (que valora donde OR'ed por el tubo |), AllowedColors Se ve como esto

myProperties.AllowedColors: 00001110

Entonces, cuando recuperas el valor, en realidad estás a nivel de bit AND 'con los valores

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

El valor None = 0

Y con respecto al uso 0 en tu enumeración, citando de msdn:

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

Use None como el nombre de la constante enumerada de indicador cuyo valor es cero. No se puede usar la constante enumerada Ninguna en una operación AND a nivel de bit para probar un indicador porque el resultado siempre es cero. Sin embargo, puede realizar una comparación lógica, no a nivel de bits, entre el valor numérico y la constante enumerada Ninguna para determinar si se han establecido los bits en el valor numérico.

Puede encontrar más información sobre el atributo de banderas y su uso en msdn y diseñando banderas en msdn


1752
2017-08-12 05:10



También puedes hacer esto

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

Encuentro que el cambio de bit es más fácil que escribir 4,8,16,32 y así sucesivamente. No tiene ningún impacto en su código porque todo está hecho en tiempo de compilación


670
2017-08-12 04:37



Combinando respuestas https://stackoverflow.com/a/8462/1037948 (declaración mediante cambio de bit) y https://stackoverflow.com/a/9117/1037948 (usando combinaciones en la declaración) puede cambiar el bit de valores previos en lugar de usar números. No necesariamente lo recomiendo, sino que solo lo señala.

Más bien que:

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

Usted puede 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 con LinqPad:

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

Resultados en:

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

86
2018-05-16 15:03



Consulte lo siguiente para ver un ejemplo que muestra la declaración y el 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");
            }
        }
    }
}

44
2017-08-12 04:32



yo preguntado recientemente sobre algo similar.

Si usa banderas, puede agregar un método de extensión a enumeraciones para facilitar la verificación de las banderas contenidas (consulte la publicación para obtener más información).

Esto le permite hacer:

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

Entonces puedes hacer:

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

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

Encuentro esto más fácil de leer que la mayoría de las maneras de verificar las banderas incluidas.


30
2017-08-12 18:40



@Nidonocu

Para agregar otro marcador a un conjunto existente de valores, use el operador de asignación OR.

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

19
2017-08-12 15:37



En extensión a la respuesta aceptada, en C # 7 las banderas de enumeración se pueden escribir usando literales binarios:

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

Creo que esta representación deja en claro cómo funcionan las banderas debajo de las sábanas.


16
2017-07-20 11:09



Para agregar Mode.Write:

Mode = Mode | Mode.Write;

15
2017-08-12 05:59



Hay algo demasiado detallado para mí sobre el if ((x & y) == y)... construir, especialmente si x Y y son conjuntos compuestos de banderas y solo quieres saber si hay alguna superposición.

En este caso, todo lo que necesitas saber es si hay un valor distinto de cero [1] después de que hayas enmascarado de bits.

[1] Ver el comentario de Jaime. Si fuéramos auténticamente enmascaramiento de bit, mie   solo es necesario verificar que el resultado fue positivo. Pero desde enums   puede ser negativo, incluso, extrañamente, cuando se combina con el [Flags]   atributo,   es defensivo para codificar != 0 más bien que > 0.

Construyendo fuera de la configuración de @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();
        }
    }
}

13
2018-05-01 23:57