Que signifie L'attribut [Flags] Enum dans C#?

de temps en temps je vois un enum comme le suivant:

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

Je ne comprends pas ce que fait exactement l'attribut [Flags] .

Quelqu'un a-t-il une bonne explication ou un bon exemple à publier?

1158
demandé sur Vogel612 2008-08-12 08:09:16

10 réponses

l'attribut flags doit être utilisé chaque fois que l'énumérable représente une collection de flags, plutôt qu'une seule valeur. De telles collections sont généralement manipulées en utilisant des opérateurs bitwise, par exemple:

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

notez que [Flags] à lui seul ne change pas ce du tout - tout ce qu'il fait est permettre une belle représentation par la .ToString() méthode:

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"

Il est également important de noter que [Flags] ne fait pas automatiquement les puissances des valeurs enum de deux. Si vous omettez les valeurs numériques, l'enum ne fonctionnera pas comme on pourrait s'y attendre dans les opérations bitwise, parce que par défaut les valeurs commencent avec 0 et increment.

déclaration incorrecte:

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

les valeurs, si déclarées de cette façon, seront jaunes = 0, vertes = 1, rouges = 2, bleues = 3. Cela le rendra inutile pour l'utilisation comme drapeaux.

voici un exemple de déclaration correcte:

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

Pour récupérer les valeurs distinctes dans votre propriété, on peut faire ceci:

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

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

ou, dans .NET 4 et plus tard:

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

sous les couvertures

cela fonctionne parce que vous avez déjà utilisé des pouvoirs de deux dans votre énumération. Sous les couvertures, vos valeurs d'énumération ressembler ceci (présenté sous forme d'octets, qui a 8 bits qui peuvent être des 1 ou des 0)

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

de même, après que vous avez placé votre propriété AllowedColors rouge, vert et bleu (quelles valeurs où OU'ed par la pipe/), AllowedColors ressemble à ceci

myProperties.AllowedColors: 00001110

donc quand vous récupérez la valeur vous êtes en fait bitwise et'ing les valeurs

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

le Aucun = 0 valeur

et en ce qui concerne l'utilisation 0 dans votre énumération, citant de msdn:

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

N'utiliser Aucun comme nom de la constante énumérée du drapeau dont la valeur est zéro. vous ne pouvez pas utiliser la constante aucun énuméré dans un bit et le fonctionnement pour tester pour un drapeau parce que le résultat est toujours zéro. cependant, vous pouvez effectuer une logique, pas un bitwise, comparaison entre la valeur numérique et la constante None unenumerated pour déterminer si des bits dans la valeur numérique sont définis.

vous pouvez trouver plus d'informations sur l'attribut flags et son utilisation à msdn et conception de drapeaux à msdn

1795
répondu andnil 2017-06-28 00:12:02

, Vous pouvez aussi le faire

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

je trouve le bit-shifting plus facile que de taper 4, 8, 16, 32 et ainsi de suite. Cela n'a aucun impact sur votre code car tout est fait au moment de la compilation

684
répondu Orion Edwards 2014-06-18 02:45:27

Combining answers https://stackoverflow.com/a/8462/1037948 (déclaration par bit-shifting) et https://stackoverflow.com/a/9117/1037948 (en utilisant des combinaisons dans la déclaration) vous pouvez bit-shift valeurs précédentes plutôt que d'utiliser des nombres. Je ne le recommande pas nécessairement, mais je vous signale que vous le pouvez.

plutôt 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,
}

vous pouvez déclarer

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

confirmation avec LinqPad:

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

résultats dans:

None = 0
One = 1
Two = 2
OneAndTwo = 3
Three = 4
OneTwoAndThree = 7
Four = 8
87
répondu drzaus 2017-05-23 10:31:37

veuillez consulter l'exemple suivant qui montre la déclaration et l'utilisation potentielle:

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
répondu OJ. 2016-08-14 02:23:29

I a demandé récemment à propos de quelque chose de similaire.

si vous utilisez des drapeaux, vous pouvez ajouter une méthode d'extension à enums pour faciliter la vérification des drapeaux contenus (voir post pour plus de détails)

cela vous permet de faire:

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

alors vous pouvez faire:

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

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

je trouve cela plus facile à lire que la plupart des façons de vérifier les drapeaux inclus.

30
répondu Keith 2017-05-23 12:34:54

@Nidonocu

Pour ajouter un autre indicateur à un ensemble de valeurs, utilisez l'opérateur d'affectation.

Mode = Mode.Read;
//Add Mode.Write
Mode |= Mode.Write;
Assert.True(((Mode & Mode.Write) == Mode.Write)
  && ((Mode & Mode.Read) == Mode.Read)));
19
répondu steve_c 2008-08-12 15:37:42

en extension à la réponse acceptée, dans C#7 les drapeaux enum peuvent être écrits en utilisant des lettres binaires:

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

je pense que cette représentation montre clairement comment les drapeaux fonctionnent sous les couvertures .

17
répondu Thorkil Holm-Jacobsen 2017-07-20 11:09:19

pour ajouter Mode.Write :

Mode = Mode | Mode.Write;
16
répondu David Wengier 2016-04-14 05:46:00

il y a quelque chose d'exagérément verbeux pour moi au sujet de la construction if ((x & y) == y)... , surtout si x et y sont deux ensembles composés de drapeaux et vous voulez seulement savoir s'il y a tout chevauchement .

dans ce cas, tout ce que vous devez savoir est s'il y a une valeur non-nulle[1] après avoir bitmasked .

[1] voir le commentaire de Jaime. Si nous étions authentiques bitmasking , il suffit de vérifier que le résultat était positif. Mais depuis enum s peut être négatif, même, étrangement, lorsqu'il est combiné avec le [Flags] attribut , c'est défensif de coder pour != 0 plutôt que > 0 .

Construit à partir de la configuration 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
répondu ruffin 2013-08-16 13:40:31
Les drapeaux

vous permettent d'utiliser bitmasking à l'intérieur de votre énumération. Cela vous permet de combiner des valeurs d'énumération, tout en conservant celles qui sont spécifiées.

[Flags]
public enum DashboardItemPresentationProperties : long
{
    None = 0,
    HideCollapse = 1,
    HideDelete = 2,
    HideEdit = 4,
    HideOpenInNewWindow = 8,
    HideResetSource = 16,
    HideMenu = 32
}
10
répondu Jay Mooney 2016-02-07 18:21:45