Domanda

Ho un enum di flag qui sotto.

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

Non riesco a rendere l'istruzione if valutata come vera.

FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2;

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

Come posso renderlo vero?

È stato utile?

Soluzione

In .NET 4 c'è un nuovo metodo Enum.HasFlag.Questo ti permette di scrivere:

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

che è molto più leggibile, IMO.

L'origine .NET indica che esegue la stessa logica della risposta accettata:

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); 
}

Altri suggerimenti

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

(testItem & FlagTest.Flag1) è un'operazione AND bit a bit.

FlagTest.Flag1 è equivalente a 001 con l'enumerazione di OP.Ora diciamo testItem ha Flag1 e Flag2 (quindi è bit a bit 101):

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

Per coloro che hanno difficoltà a visualizzare ciò che sta accadendo con la soluzione accettata (che è questa),

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

testItem (come da domanda) è definito come,

testItem 
 = flag1 | flag2  
 = 001 | 010  
 = 011

Quindi, nell'istruzione if, il lato sinistro del confronto è:

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

E l'istruzione if completa (che restituisce true if flag1 è situato in testItem),

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

@phil-devaney

Si noti che, tranne nei casi più semplici, il Enum.HasFlag comporta una pesante penalizzazione delle prestazioni rispetto alla scrittura manuale del codice.Considera il seguente codice:

[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();
    }
}

Oltre 10 milioni di iterazioni, il metodo di estensione HasFlags impiega ben 4793 ms, rispetto ai 27 ms dell'implementazione bit a bit standard.

Ho impostato un metodo di estensione per farlo: domanda correlata.

Fondamentalmente:

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

Quindi puoi fare:

FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2;

if( testItem.IsSet ( FlagTests.Flag1 ) )
    //Flag1 is set

Per inciso, la convenzione che utilizzo per le enumerazioni è singolare per standard, plurale per flag.In questo modo sai dal nome dell'enumerazione se può contenere più valori.

Un altro consiglio...Non eseguire mai il controllo binario standard con il flag il cui valore è "0".Il tuo controllo su questa bandiera sarà sempre vero.

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

Se controlli binario il parametro di input rispetto a FullInfo, ottieni:

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

bPRez sarà sempre vero come QUALSIASI COSA e 0 sempre == 0.


Dovresti invece semplicemente verificare che il valore dell'input sia 0:

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

Per le operazioni bit è necessario utilizzare operatori bit a bit.

Questo dovrebbe fare al caso tuo:

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

Modificare: Risolto il problema con il controllo if: sono tornato ai miei modi C/C++ (grazie a Ryan Farley per averlo sottolineato)

Per quanto riguarda la modifica.Non puoi renderlo vero.Ti suggerisco di racchiudere ciò che desideri in un'altra classe (o metodo di estensione) per avvicinarti alla sintassi di cui hai bisogno.

cioè.

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

Prova questo:


if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // do something
}
Fondamentalmente, il tuo codice chiede se avere entrambi i flag impostati equivale ad avere un flag impostato, il che è ovviamente falso.Il codice precedente lascerà impostato solo il bit Flag1, se è impostato, quindi confronterà questo risultato con Flag1.

anche senza [Flags], potresti usare qualcosa di simile

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

o se hai un'enumerazione con valore zero

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top