Comment comparer les drapeaux en C #?

J’ai un code enum ci-dessous.

[Flags] public enum FlagTest { None = 0x0, Flag1 = 0x1, Flag2 = 0x2, Flag3 = 0x4 } 

Je ne peux pas faire la déclaration if pour évaluer à true.

 FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2; if (testItem == FlagTest.Flag1) { // Do something, // however This is never true. } 

Comment puis-je rendre cela vrai?

Dans .NET 4, il existe une nouvelle méthode Enum.HasFlag . Cela vous permet d’écrire:

 if ( testItem.HasFlag( FlagTest.Flag1 ) ) { // Do Stuff } 

ce qui est beaucoup plus lisible, l’OMI.

La source .NET indique que cette logique est identique à la réponse acceptée:

 public Boolean HasFlag(Enum flag) { if (!this.GetType().IsEquivalentTo(flag.GetType())) { throw new ArgumentException( Environment.GetResourceSsortingng( "Argument_EnumTypeDoesNotMatch", flag.GetType(), this.GetType())); } ulong uFlag = ToUInt64(flag.GetValue()); ulong uThis = ToUInt64(GetValue()); // test predicate return ((uThis & uFlag) == uFlag); } 
 if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // Do something } 

(testItem & FlagTest.Flag1) est une opération ET bit à bit.

FlagTest.Flag1 est équivalent à 001 avec l’énumération OP. Maintenant, disons que testItem a Flag1 et Flag2 (donc 101 du bit):

  001 &101 ---- 001 == FlagTest.Flag1 

Pour ceux qui ont du mal à visualiser ce qui se passe avec la solution acceptée (qui est la suivante),

 if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // Do stuff. } 

testItem (selon la question) est défini comme suit:

 testItem = flag1 | flag2 = 001 | 010 = 011 

Ensuite, dans l’instruction if, le côté gauche de la comparaison est,

 (testItem & flag1) = (011 & 001) = 001 

Et l’instruction if complète (évaluée à true si flag1 est défini dans testItem ),

 (testItem & flag1) == flag1 = (001) == 001 = true 

@ phil-devaney

Notez que, sauf dans les cas les plus simples, Enum.HasFlag comporte une lourde pénalité en termes de performances par rapport à l’écriture manuelle du code. Considérez le code suivant:

 [Flags] public enum TestFlags { One = 1, Two = 2, Three = 4, Four = 8, Five = 16, Six = 32, Seven = 64, Eight = 128, Nine = 256, Ten = 512 } class Program { static void Main(ssortingng[] args) { TestFlags f = TestFlags.Five; /* or any other enum */ bool result = false; Stopwatch s = Stopwatch.StartNew(); for (int i = 0; i < 10000000; i++) { result |= f.HasFlag(TestFlags.Three); } s.Stop(); Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms* s.Restart(); for (int i = 0; i < 10000000; i++) { result |= (f & TestFlags.Three) != 0; } s.Stop(); Console.WriteLine(s.ElapsedMilliseconds); // *27 ms* Console.ReadLine(); } } 

Plus de 10 millions d’itérations, la méthode d’extension HasFlags prend 4793 ms, contre 27 ms pour la mise en œuvre standard.

J’ai mis en place une méthode d’extension pour le faire: question associée .

Fondamentalement:

 public static bool IsSet( this Enum input, Enum matchTo ) { return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0; } 

Ensuite, vous pouvez faire:

 FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2; if( testItem.IsSet ( FlagTests.Flag1 ) ) //Flag1 is set 

Incidemment, la convention que j’utilise pour les énumérations est singulière pour le standard, plurielle pour les drapeaux. De cette manière, vous savez si le nom enum peut contenir plusieurs valeurs.

Un autre conseil … Ne faites jamais la vérification binary standard avec le drapeau dont la valeur est “0”. Votre chèque sur ce drapeau sera toujours vrai.

 [Flags] public enum LevelOfDetail { [EnumMember(Value = "FullInfo")] FullInfo=0, [EnumMember(Value = "BusinessData")] BusinessData=1 } 

Si vous vérifiez le paramètre d’entrée binary avec FullInfo – vous obtenez:

 detailLevel = LevelOfDetail.BusinessData; bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo; 

bPRez sera toujours vrai comme tout & 0 toujours == 0.


Au lieu de cela, vous devez simplement vérifier que la valeur de l’entrée est 0:

 bool bPRez = (detailLevel == LevelOfDetail.FullInfo); 
 if((testItem & FlagTest.Flag1) == FlagTest.Flag1) { ... } 

Essaye ça:

 if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // do something } 

Fondamentalement, votre code demande si avoir les deux indicateurs définis est identique à avoir un drapeau défini, ce qui est évidemment faux. Le code ci-dessus ne laissera que le bit Flag1 défini s’il est défini, puis compare ce résultat à Flag1.

Pour les opérations sur bit, vous devez utiliser des opérateurs binarys.

Cela devrait faire l’affaire:

 if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) { // Do something, // however This is never true. } 

Edit: Correction de mon contrôle if – Je suis revenu à mes méthodes C / C ++ (merci à Ryan Farley de l’avoir signalé)

En ce qui concerne le assembly Vous ne pouvez pas le rendre vrai. Je vous suggère d’emballer ce que vous voulez dans une autre classe (ou méthode d’extension) pour vous rapprocher de la syntaxe dont vous avez besoin.

c’est à dire

 public class FlagTestCompare { public static bool Compare(this FlagTest myFlag, FlagTest condition) { return ((myFlag & condition) == condition); } } 

même sans [Drapeaux], vous pourriez utiliser quelque chose comme ça

 if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=0){ //.. } 

ou si vous avez un enum de valeur zéro

 if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){ //.. }