Pregunta

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

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

No entiendo qué es exactamente la [Flags]-atributo hace.

Alguien tiene una buena explicación o ejemplo, se puede publicar?

¿Fue útil?

Solución

El [Flags] atributo debe ser utilizado siempre que el enumerable representa una colección de valores posibles, en lugar de un único valor.Tales colecciones se utilizan a menudo con operadores bit a bit, por ejemplo:

var allowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;

Tenga en cuenta que el [Flags] atributo no habilitar esta por sí misma de todo lo que hace es permitir una buena representación por la .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 automáticamente realizar la enumeración de los valores de potencias de dos.Si se omiten los valores numéricos, la enumeración no funciona como cabría esperar en las operaciones bit a bit, ya que por defecto los valores de inicio con el 0 y el incremento.

Declaración incorrecta:

[Flags]
public enum MyColors
{
    Yellow,  // 0
    Green,   // 1
    Red,     // 2
    Blue     // 3
}

Los valores, si se ha declarado esta manera, Amarillo = 0, Verde = 1, Rojo = 2, Azul = 3.Esto hace que sea inútil como banderas.

He aquí un ejemplo de una declaración correcta:

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

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

if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
{
    // Yellow is allowed...
}

o antes .NET 4:

if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
{
    // Yellow is allowed...
}

if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
{
    // Green is allowed...
}    

Debajo de las mantas

Esto funciona debido a que se utilizan potencias de dos en su enumeración.Debajo de las mantas, la enumeración de los valores de este aspecto en el sistema binario de unos y ceros:

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

Del mismo modo, después de que hayas establecido tu propiedad AllowedColors para Rojo, Verde y Azul usando los binarios bit a bit O | operador, AllowedColors se parece a esto:

myProperties.AllowedColors: 00001110

Así que a la hora de recuperar el valor que se realizan a nivel de bit Y & en los valores:

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

Ninguno = 0 valor

Y sobre el uso de 0 en su enumeración, citando de MSDN:

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

Uso Ninguno como el nombre de la bandera enumerados constante cuyo valor es cero. Usted no puede utilizar Ninguna de las constantes enumeradas en una operación and bit a bit para la prueba de una bandera, porque el resultado siempre es cero. Sin embargo, usted puede realizar una operación lógica, no un bit a bit, la comparación entre el valor numérico y la constante enumerada None para determinar si alguno de los bits del valor numérico se establecen.

Usted puede encontrar más información sobre el atributo flags y su uso en msdn y el diseño de las banderas en msdn

Otros consejos

También se puede hacer esto

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

Me parece el desplazamiento de bits más fácil de escribir 4,8,16,32 y así sucesivamente.No tiene ningún impacto sobre el código porque todo se hace en tiempo de compilación

La combinación de respuestas https://stackoverflow.com/a/8462/1037948 (declaración a través de desplazamiento de bits) y https://stackoverflow.com/a/9117/1037948 (el uso de combinaciones en la declaración) puede bit de cambio de los valores anteriores en lugar de usar números.No necesariamente recomendando, pero sólo señalando puede.

En lugar 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,
}

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

Por favor, consulte el siguiente para un ejemplo que muestra la declaración y el potencial de uso de:

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

Yo preguntó recientemente acerca de algo similar.

Si el uso de indicadores puede agregar un método de extensión para las enumeraciones para hacer la comprobación de los indicadores más fácil (ver post para el detalle)

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 usted puede hacer:

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

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

Esto me parece más fácil de leer que la mayoría de los medios de comprobación de la incluyó banderas.

En la extensión a la aceptación de la respuesta, en C#7 la enumeración banderas puede ser escrito utilizando binario literales:

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

Creo que esta representación pone de manifiesto que los indicadores de trabajo debajo de las mantas.

@Nidonocu

Para añadir otro indicador a un conjunto existente de los valores, utilizar el operador de asignación.

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

Para agregar Mode.Write:

Mode = Mode | Mode.Write;

Hay algo demasiado detallado para mí acerca de la if ((x & y) == y)... construir, sobre todo si x Y y ambos están compuestos por conjuntos de banderas y sólo quiero saber si hay cualquier se superponen.

En este caso, todo lo que usted realmente necesita saber es si hay un valor que no sea cero[1] después de bitmasked.

[1] Véase Jaime el comentario.Si hemos sido auténticamente bitmasking, nos gustaría sólo se necesita comprobar que el resultado fue positivo.Pero ya enums puede ser negativo, incluso, curiosamente, cuando se combina con el [Flags] atributo, es defensiva de código para != 0 en lugar de > 0.

Edificio de @andnil de instalación de...

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

Banderas de permitir el uso de bitmasking en el interior de su enumeración.Esto le permite combinar los valores de enumeración, mientras que la retención de los que se especifican.

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

Cuando se trabaja con banderas a menudo me declaro adicional Ninguno, y Todos los elementos.Estos son útiles para comprobar si todos los indicadores que se establecen o no se fija ningún indicador.

[Flags] 
enum SuitsFlags { 

    None =     0,

    Spades =   1 << 0, 
    Clubs =    1 << 1, 
    Diamonds = 1 << 2, 
    Hearts =   1 << 3,

    All =      ~(~0 << 4)

}

Uso:

Spades | Clubs | Diamonds | Hearts == All  // true
Spades & Clubs == None  // true
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top