2008-09-02 13 views
133

Ich habe eine Flagge enum unten.Wie vergleicht man Flags in C#?

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

Ich kann nicht die if-Anweisung als wahr bewerten.

FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2; 

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

Wie kann ich das wahr machen?

+0

mich korrigieren, wenn ich falsch bin, 0 angemessen zu sein als Flag-Wert verwendet? – Roylee

+3

@Roylee: 0 ist akzeptabel, und es ist eine gute Idee, eine "None" oder "Undefined" -Flag zu haben, um zu testen, keine Flags gesetzt. Es ist keineswegs erforderlich, aber es ist eine gute Übung. Das Wichtigste, woran man sich erinnert, wird von Leonid in seiner Antwort hervorgehoben. – Andy

+4

@Roylee Es wird tatsächlich von Microsoft empfohlen, ein Flag "None" mit einem Wert von Null bereitzustellen. Siehe http://msdn.microsoft.com/en-us/library/vstudio/ms229058(v=vs.100).aspx – ThatMatthew

Antwort

289

in .NET-4 gibt es ein neues Verfahren Enum.HasFlag. Dies ermöglicht es Ihnen zu schreiben:

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

die viel besser lesbar ist, IMO.

Die.NET-Quelle gibt an, dass diese die gleiche Logik wie die akzeptierte Antwort führt:

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

Ah, endlich etwas aus der Box. Das ist großartig, ich habe lange auf dieses relativ einfache Feature gewartet. Froh, dass sie sich entschieden haben, es rein zu lassen. –

+8

Beachten Sie jedoch, dass die Antwort unten Leistungsprobleme mit dieser Methode zeigt - das könnte ein Problem für einige Leute sein. Zum Glück nicht für mich. –

+1

Die Leistungsüberlegung für diese Methode ist Boxen, da es Argumente als eine Instanz der 'Enum' Klasse nimmt. –

4

Für Bitoperationen müssen Sie bitweise Operatoren verwenden.

Dies sollte den Trick:

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

Edit: meine Fest wenn Check - ich (es für den Hinweis auf dank Ryan Farley)

171
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{ 
    // Do something 
} 
zurück in mein C/C++ Wege schlüpfte

(testItem & FlagTest.Flag1) ist eine bitweise UND-Operation.

FlagTest.Flag1 entspricht 001 mit OP-Enum. Nun lassen Sie uns sagen, testItem hat Flag1 und Flag2 (es ist also bitweise 101):

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

Was genau ist die Logik hier? Warum muss das Prädikat so geschrieben werden? –

+3

@ IanR.O'Brien Flag1 | Flag 2 wird in 001 oder 010 übersetzt, was dasselbe wie 011 ist, jetzt, wenn Sie eine Gleichheit von diesem 011 == Flag1 oder übersetzten 011 == 001 machen, das immer false zurückgibt. Wenn Sie nun ein bitweises AND mit Flag1 ausführen, wird es in 011 AND 001 übersetzt, was 001 zurückgibt, wobei die Gleichheitsrückgaben nun wahr sind, weil 001 == 001 – pqsk

+0

Dies ist die beste Lösung, da HasFlags viel ressourcenintensiver ist. Und alles, was HasFlags macht, wird hier vom Compiler –

7
if((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{ 
... 
} 
4

Versuchen Sie folgendes:


if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{ 
    // do something 
} 
Grundsätzlich Ihr Code gefragt werden, ob beide Flags gesetzt, die die gleiche ist ein Flag gesetzt haben, Das ist offensichtlich falsch. Der obige Code lässt nur das Flag1-Bit gesetzt, wenn es überhaupt gesetzt ist, und vergleicht dann dieses Ergebnis mit Flag1.

21

Ich habe eine Erweiterungs-Methode eingerichtet, um es zu tun: related question.

Grundsätzlich gilt:

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

Dann können Sie tun:

FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2; 

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

übrigens die Konvention ich für Aufzählungen verwenden ist einzigartig für Standard-, Plural für Fahnen. Auf diese Weise wissen Sie anhand des Namens, ob es mehrere Werte enthalten kann.

+0

Dies sollte ein Kommentar sein, aber da ich ein neuer Benutzer bin, sieht es so aus, als ob ich noch keine Kommentare hinzufügen kann ... public static bool isset (dieser Enum-Eingang, Enum matchTo) { return (Convert.ToUInt32 (Eingang) & Convert.ToUInt32 (matchTo))! = 0; } Gibt es eine Möglichkeit mit jeder Art von ENUM-kompatibel sein (denn hier wird es nicht funktionieren, wenn Ihr Enum vom Typ UInt64 ist oder negative Werte annehmen kann)? – user276648

+0

Dies ist ziemlich redundant mit Enum.HasFlag (Enum) (verfügbar in. NET 4.0) – PPC

+1

@PPC Ich würde nicht sagen, redundante genau - viele Leute entwickeln auf älteren Versionen des Frameworks. Sie haben jedoch recht, .Net 4-Benutzer sollten stattdessen die 'HasFlag'-Erweiterung verwenden. – Keith

3

In Bezug auf die Bearbeitung. Du kannst es nicht wahr machen. Ich schlage vor, dass Sie das, was Sie wollen, in eine andere Klasse (oder Erweiterungsmethode) einbinden, um näher an die von Ihnen benötigte Syntax zu gelangen.

d.h.

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

Für diejenigen, die Schwierigkeiten haben, zu visualisieren, was mit der akzeptierten Lösung geschieht (was das ist),

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

testItem (gemäß der Frage) ist wie folgt definiert,

testItem 
= flag1 | flag2 
= 001 | 010 
= 011 

Dann wird in der iF-Anweisung ist die linke Seite des Vergleichs,

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

und die volle if-Anweisung (dh den Wert true, wenn flag1 in testItem gesetzt),

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

Ein weiterer Ratschlag ... tun Sie nie die Standardbinärdistributionen Prüfung mit der Flagge, deren Wert ist "0". Ihre Überprüfung dieser Flagge wird immer wahr sein.

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

Wenn Sie binäre Kontrolleingabeparameter gegen FullInfo - Sie erhalten:

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

bPRez wird immer wahr sein, wie ANYTHING & 0 immer == 0.


Stattdessen sollten Sie einfach überprüfen, ob der Wert des Eingangs 0 ist:

bool bPRez = (detailLevel == LevelOfDetail.FullInfo); 
+0

Ich habe gerade einen solchen 0-Flag-Fehler behoben. Ich denke, das ist ein Entwurfsfehler in .NET Framework (3.5), weil Sie wissen müssen, welcher der Flag-Werte 0 ist, bevor Sie es testen. – thersch

21

@ phil-devaney

Beachten Sie, dass außer in den einfachsten Fällen die Enum.HasFlag eine schwere Leistungseinbußen im Vergleich trägt manuell den Code zum Schreiben aus. Betrachten Sie den folgenden Code ein:

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

über 10 Millionen Iterationen erfolgt die HasFlags Extension-Methode einen satten 4793 ms, im Vergleich zu dem 27 ms für die Standard-bitweise Implementierung.

+5

In der Tat. Wenn Sie sich die Implementierung von HasFlag anschauen, werden Sie sehen, dass sie einen "GetType()" für beide Operanden ausführt, was ziemlich langsam ist. Dann tut es "Enum.ToUInt64 (value.GetValue());" auf beiden Operanden vor der bitweisen Prüfung. – user276648

+0

Dies ist ein großer Leistungsunterschied für einen sehr kleinen Funktionsgewinn. Es lohnt sich, mit HasFlag() – PPC

+1

zu stoppen Ich habe Ihren Test mehrmals ausgeführt und bekam ~ 500ms für HasFlags und ~ 32ms für bitweise. Obwohl BitFlash noch eine Größenordnung schneller ist, war HasFlags eine Größenordnung niedriger als Ihr Test. (Lief der Test auf einem 2.5 GHz Core i3 und .NET 4.5) – MarioVW

1

auch ohne [Flags], Sie so etwas wie dieses

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

verwenden könnte, oder wenn Sie einen Nullwert Enum haben

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