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?
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
, 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
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
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");
}
}
}
}
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.
@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)));
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 .
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();
}
}
}
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
}