Question

J'ai une énumération de drapeau ci-dessous.

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

Je ne peux pas faire en sorte que l'instruction if soit évaluée comme vraie.

FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2;

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

Comment puis-je rendre cela vrai ?

Était-ce utile?

La solution

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

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

ce qui est beaucoup plus lisible, OMI.

La source .NET indique que cela exécute la même logique que la réponse acceptée :

public Boolean HasFlag(Enum flag) {
    if (!this.GetType().IsEquivalentTo(flag.GetType())) {
        throw new ArgumentException(
            Environment.GetResourceString(
                "Argument_EnumTypeDoesNotMatch", 
                flag.GetType(), 
                this.GetType()));
    }

    ulong uFlag = ToUInt64(flag.GetValue()); 
    ulong uThis = ToUInt64(GetValue());
    // test predicate
    return ((uThis & uFlag) == uFlag); 
}

Autres conseils

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

(testItem & FlagTest.Flag1) est une opération ET au niveau du bit.

FlagTest.Flag1 est équivalent à 001 avec l'énumération de OP.Maintenant disons testItem a Flag1 et Flag2 (donc c'est au niveau du bit 101):

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

Pour ceux qui ont du mal à visualiser ce qui se passe avec la solution acceptée (c'est-à-dire ceci),

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

testItem (selon la question) est défini comme,

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 (qui est évaluée à vrai si flag1 est placé dans testItem),

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

@phil-devaney

Notez que sauf dans les cas les plus simples, le Enum.HasFlag entraîne 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(string[] 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();
    }
}

Sur plus de 10 millions d'itérations, la méthode d'extension HasFlags prend 4 793 ms, contre 27 ms pour l'implémentation standard au niveau du bit.

J'ai mis en place une méthode d'extension pour le faire : question connexe.

Essentiellement:

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

Soit dit en passant, la convention que j'utilise pour les énumérations est au singulier pour le standard, au pluriel pour les drapeaux.De cette façon, vous savez grâce au nom de l'énumération s'il peut contenir plusieurs valeurs.

Encore un conseil...Ne faites jamais la vérification binaire standard avec le drapeau dont la valeur est "0".Votre contrôle sur ce drapeau sera toujours vrai.

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

Si vous vérifiez binairement le paramètre d'entrée par rapport à 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) 
{
...
}

Pour les opérations sur les bits, vous devez utiliser des opérateurs au niveau du bit.

Cela devrait faire l'affaire:

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

Modifier: Correction de ma vérification if - je suis revenu à mes méthodes C/C++ (merci à Ryan Farley de l'avoir signalé)

Concernant la modification.Vous ne pouvez pas rendre cela vrai.Je vous suggère d'envelopper 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);
    }
}

Essaye ça:


if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // do something
}
Fondamentalement, votre code demande si le fait d'avoir les deux indicateurs définis équivaut à avoir un seul indicateur, ce qui est évidemment faux.Le code ci-dessus ne laissera que le bit Flag1 défini s'il est défini, puis comparera ce résultat à Flag1.

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

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

ou si vous avez une énumération de valeur zéro

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top