1401 votes

Que signifie l'attribut Enum [Flags] en C# ?

Quelqu'un a-t-il une bonne explication ou un exemple qu'il pourrait poster ?

Edit : J'ai changé la réponse, celle-ci est plus approfondie.

2099voto

andnil Points 8179

L'attribut flags doit être utilisé lorsque l'énumérable représente une collection de drapeaux, plutôt qu'une valeur unique. Ces collections sont généralement manipulées à l'aide d'opérateurs binaires, par exemple :

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

Notez que [Flags] en soi ne change rien du tout - tout ce qu'il fait, c'est permettre une belle représentation par le .ToString() méthode :

[Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
enum Suits { 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] n'est pas rend automatiquement les valeurs de l'énumération des puissances de deux. Si vous omettez les valeurs numériques, l'énumération ne fonctionnera pas comme on pourrait s'y attendre dans les opérations de type bitwise, car par défaut les valeurs commencent par 0 et s'incrémentent.

Déclaration incorrecte :

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

Les valeurs, si elles sont déclarées de cette façon, seront Jaune = 0, Vert = 1, Rouge = 2, Bleu = 3. Cela les rendra inutilisables en tant que 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,

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

Sous les couvertures

Cela fonctionne parce que vous avez précédemment utilisé des multiples de deux dans votre énumération. En réalité, les valeurs de votre énumération ressemblent à ceci (présentées sous forme d'octets, qui ont 8 bits qui peuvent être des 1 ou des 0)

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

De même, après avoir défini votre propriété Couleurs autorisées à Rouge, Vert et Bleu (ces valeurs sont mises sous OR par le pipe |), Couleurs autorisées ressemble à ceci

myProperties.AllowedColors: 00001110

Donc, lorsque vous récupérez la valeur, vous êtes en fait en train de faire un ET binaire des valeurs.

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

La valeur None = 0

Et concernant l'utilisation de 0 dans votre énumération, je cite le msdn :

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

Utilisez None comme nom de la constante énumérée du drapeau dont la valeur est zéro. Vous ne pouvez pas utiliser la constante énumérée None dans une opération ET par bit pour tester un indicateur car le résultat est toujours zéro. Toutefois, vous pouvez effectuer une comparaison logique, et non bit à bit, entre la valeur numérique et la constante énumérée None pour déterminer si l'un des bits de la valeur numérique est activé.

Vous trouverez de plus amples informations sur l'attribut flags et son utilisation à l'adresse suivante msdn y conception de drapeaux à msdn

764voto

Orion Edwards Points 54939

Vous pouvez également le faire

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

Je trouve le décalage des bits 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.

113voto

drzaus Points 3344

Combiner les réponses http://stackoverflow.com/a/8462/1037948 (déclaration par décalage de bits) et http://stackoverflow.com/a/9117/1037948 (en utilisant des combinaisons dans la déclaration) vous pouvez décaler les valeurs précédentes en bits plutôt que d'utiliser des chiffres. Je ne le recommande pas nécessairement, mais je signale simplement que c'est possible.

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

48voto

OJ. Points 16939

Veuillez consulter l'exemple suivant qui illustre la déclaration et l'utilisation potentielle :

namespace Flags
{
    class Program
    {

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

37voto

Keith Points 46288

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

Si vous utilisez des drapeaux, vous pouvez ajouter une méthode d'extension aux enums pour faciliter la vérification des drapeaux contenus (voir le 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 le 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X