2010-09-16 5 views
10

Quando si utilizzano Enums con campi di bit:C'è un altro modo per testare i campi di bit Enum?

enum ReallyBigEnum { FirstChoice = 0x01, AnotherOption = 0x02 } 
    ReallyBigEnum flag = ReallyBigEnum.FirstChoice | ReallyBigEnum.AnotherOption; 

il codice usato per testare i bit è:

if((flag & ReallyBigEnum.AnotherOption) == ReallyBigEnum.AnotherOption) { ... } 

che sembra dettagliata e soggetto a errori a causa della necessità di ripetere il bit in fase di test.

Sarebbe bello se ci fosse un modo per dire:

if(flag.IsSet(ReallyBigEnum.AnotherOption)) { ... } 

ma Enums non supportano metodi di istanza. Così, ho provato una funzione template:

class Enums 
    { 
     public static bool IsSet<T>(T flag, T bit) { return (flag & bit) == bit; } 
    } 

ma il codice per testare i bit, allora si presenta così:

if(Enums.IsSet<ReallyBigEnum>(flag, ReallyBigEnum.AnotherOption)) { ... } 

che è un sacco di scrivere. Poi ho cercato di accorciare:

class Enums 
    { 
     public static bool IsSet(int flag, int bit) { return (flag & bit) == bit; } 
    } 

, ma poi si deve lanciare ogni valore al suo tipo di base in questo modo:

if(Enums.IsSet((int)flag, (int)ReallyBigEnum.AnotherOption)) { ... } 

che è anche un dolore per il codice e perde il vantaggio di controllo di tipo .

È possibile scrivere la stessa funzione per utilizzare i parametri "oggetto", ma dopo il tipo di oggetto e il tipo di base sottostante devono essere testati.

Quindi, sono bloccato con il modo ridondante standard nella parte superiore.

Qualcuno ha altre idee su un modo semplice e pulito per testare i campi di bit Enum?

Grazie mille.

risposta

6

Fino a .Net 3.5 questa è la tua unica opzione. In .Net 4.0 c'è un metodo HasFlag sull'enumerazione.

+0

Il metodo hasFlag() è la soluzione perfetta, naturalmente. Grazie! Sfortunatamente, il nostro ambiente di sviluppo utilizza ancora .NET 2.0. –

2
if((flag & ReallyBigEnum.AnotherOption) != 0) { ... } 

AGGIORNAMENTO:

Quanto sopra, ovviamente, funzionano solo se si sta testando un singolo bit. Se si desidera testare più bit, è necessario qualcos'altro, a seconda che si stia verificando per tutti bit o qualsiasi bit.

test che qualsiasi di un insieme di bit è impostata

Per questo caso, è sufficiente utilizzare una variante della versione singolo bit.

if((flag & (ReallyBigEnum.FirstOption | ReallyBigEnum.AnotherOption)) != 0) { ... } 

test che tutto ad un gruppo di bit è impostato

Per questo caso achive chiarezza e affidabilità, suggerisce di creare una costante che contiene tutti i bit.

const int ReallyBigEnum WickedAwesomeOptions = ReallyBigEnum.FirstOption | ReallyBigEnum.AnotherOption; 
... 
if (flag & WickedAwesomeOptions == WickedAwesomeOptions) { ... } 

Alcuni di ridondanza è ancora lì, ma non è fragile o confuse, ed è facile da mantenere.

Se la combinazione di bit si applica in modo ampio, è possibile aggiungerla all'enum stesso.

[Flags] 
enum ReallyBigEnum 
{ 
    FirstOption = 1, 
    AnotherOption = 2, 
    WickedAwesomeOptions = FirstOption | AnotherOption, 
} 
.... 
if (flag & ReallyBigEnum.WickedAwesomeOptions == ReallyBigEnum.WickedAwesomeOptions) { ... } 
+0

Rimuove la ridondanza ma funzionerà solo se tutti i flag sono bit singoli. I flag composti con più bit potrebbero restituire falsi positivi. –

+0

@Chris C. - Sicuramente funziona solo con flag a bit singolo. Questo è esattamente ciò che la domanda sta chiedendo. Nessuna parte della domanda richiede il controllo di più bit. –

+0

@Chris C. - Ho modificato la risposta per rispondere a questa nuova domanda. –

2
/// <summary> 
/// Taken from https://stackoverflow.com/questions/9033/hidden-features-of-c/407325#407325 
/// instead of doing (enum & value) == value you can now use enum.Has(value) 
/// </summary> 
/// <typeparam name="T">Type of enum</typeparam> 
/// <param name="type">The enum value you want to test</param> 
/// <param name="value">Flag Enum Value you're looking for</param> 
/// <returns>True if the type has value bit set</returns> 
public static bool Has<T>(this System.Enum type, T value) 
{ 
    return (((int)(object)type & (int)(object)value) == (int)(object)value); 
} 

Ecco un metodo di estensione l'ho presa dal above link

Usa:

MyFlagEnum e = MyFlagEnum.First | MyFlagEnum.Second; 
if(e.Has(MyFlagEnum.First)) 
{ 
    // Do stuff 
} 
+0

Oh, ma si utilizza .NET 2, quindi nessun metodo di estensione ... – PostMan

+0

Ottima soluzione. La documentazione dice che per C#, i metodi di estensione sono altrettanto efficienti dei metodi di classe originali. I metodi di estensione sono stati introdotti in .NET v3.0. Questo codice fa un'ipotesi sulla dimensione dell'Enum che può essere basata anche su interi non firmati e lunghi. –

+1

Se il cast è stato modificato in: (long) (oggetto) valore funzionerebbe per tutti i tipi interi? –