Frage Was bedeutet das Aufzählungsattribut [Flags] in C #?


Von Zeit zu Zeit sehe ich ein enum wie folgt:

[Flags]
public enum Options 
{
    None    = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    Option4 = 8
}

Ich verstehe nicht genau was [Flags]-Attribut tut es.

Hat jemand eine gute Erklärung oder ein Beispiel, das er schreiben könnte?


1123
2017-08-12 04:09


Ursprung


Antworten:


Das Flags-Attribut sollte immer dann verwendet werden, wenn das Enumerable eine Sammlung von Flags darstellt und nicht ein einzelner Wert. Solche Sammlungen werden normalerweise mit bitweisen Operatoren bearbeitet, zum Beispiel:

myProperties.AllowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;

Beachten Sie, dass [Flags] an sich ändert sich das nicht überhaupt - Alles was es macht, ist eine schöne Darstellung durch die .ToString() Methode:

enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
[Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }

...

var str1 = (Suits.Spades | Suits.Diamonds).ToString();
           // "5"
var str2 = (SuitsFlags.Spades | SuitsFlags.Diamonds).ToString();
           // "Spades, Diamonds"

Es ist auch wichtig, dies zu beachten [Flags]  nicht die Enum-Werte automatisch zu Zweierpotenzen machen. Wenn Sie die numerischen Werte weglassen, funktioniert die Enumeration nicht wie erwartet bei bitweisen Operationen, da die Werte standardmäßig mit 0 beginnen und inkrementieren.

Falsche Deklaration:

[Flags]
public enum MyColors
{
    Yellow,
    Green,
    Red,
    Blue
}

Die Werte, wenn sie auf diese Weise deklariert werden, sind Gelb = 0, Grün = 1, Rot = 2, Blau = 3. Dadurch wird es für die Verwendung als Flags unbrauchbar.

Hier ist ein Beispiel für eine korrekte Deklaration:

[Flags]
public enum MyColors
{
    Yellow = 1,
    Green = 2,
    Red = 4,
    Blue = 8
}

Um die unterschiedlichen Werte in Ihrer Eigenschaft abzurufen, können Sie Folgendes tun:

if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
{
    // Yellow has been set...
}

if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
{
    // Green has been set...
}    

oder in .NET 4 und höher:

if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
{
    // Yellow has been set...
}

Unter der Decke

Dies funktioniert, weil Sie zuvor Potenzen von zwei in Ihrer Aufzählung verwendet haben. Unter dem Deckblatt sehen Ihre Aufzählungswerte so aus (dargestellt als Bytes, die 8 Bits haben, die 1 oder 0 sein können)

 Yellow: 00000001
 Green:  00000010
 Red:    00000100
 Blue:   00001000

Ebenso, nachdem Sie Ihre Eigenschaft festgelegt haben Erlaubte Farben zu Rot, Grün und Blau (welche Werte wo ODER von der Pfeife | | Erlaubte Farben sieht aus wie das

myProperties.AllowedColors: 00001110

Wenn Sie also den Wert abrufen, werden die Werte tatsächlich bitweise UND-verknüpft

myProperties.AllowedColors: 00001110
             MyColor.Green: 00000010
             -----------------------
                            00000010 // Hey, this is the same as MyColor.Green!

Der Wert Kein = 0

Und in Bezug auf Verwendung 0 in Ihrer Enumeration, zitiert von msdn:

[Flags]
public enum MyColors
{
    None = 0,
    ....
}

Verwenden Sie None als den Namen der Flagauflistungskonstante, deren Wert Null ist. Sie können die Aufzählungskonstante None in einer bitweisen UND-Operation nicht verwenden, um nach einem Flag zu suchen, da das Ergebnis immer Null ist. Sie können jedoch einen logischen, nicht bitweisen Vergleich zwischen dem numerischen Wert und der Aufzählungskonstanten None durchführen, um festzustellen, ob Bits im numerischen Wert gesetzt sind.

Sie können mehr Informationen über das Attribut flags und seine Verwendung bei finden msdn und Flags bei msdn entwerfen


1752
2017-08-12 05:10



Sie können dies auch tun

[Flags]
public enum MyEnum
{
    None   = 0,
    First  = 1 << 0,
    Second = 1 << 1,
    Third  = 1 << 2,
    Fourth = 1 << 3
}

Ich finde das Bit-Shifting leichter als das Tippen von 4,8,16,32 und so weiter. Es hat keinen Einfluss auf Ihren Code, da alles zur Kompilierzeit erfolgt


670
2017-08-12 04:37



Antworten kombinieren https://stackoverflow.com/a/8462/1037948 (Deklaration über Bit-Shifting) und https://stackoverflow.com/a/9117/1037948 (Verwenden Sie Kombinationen in der Deklaration) Sie können frühere Werte Bit-Shift, anstatt mit Zahlen. Ich empfehle es nicht unbedingt, aber ich kann nur darauf hinweisen, dass Sie es können.

Eher, als:

[Flags]
public enum Options : byte
{
    None    = 0,
    One     = 1 << 0,   // 1
    Two     = 1 << 1,   // 2
    Three   = 1 << 2,   // 4
    Four    = 1 << 3,   // 8

    // combinations
    OneAndTwo = One | Two,
    OneTwoAndThree = One | Two | Three,
}

Sie können deklarieren

[Flags]
public enum Options : byte
{
    None    = 0,
    One     = 1 << 0,       // 1
    // now that value 1 is available, start shifting from there
    Two     = One << 1,     // 2
    Three   = Two << 1,     // 4
    Four    = Three << 1,   // 8

    // same combinations
    OneAndTwo = One | Two,
    OneTwoAndThree = One | Two | Three,
}

Bestätigung mit LinqPad:

foreach(var e in Enum.GetValues(typeof(Options))) {
    string.Format("{0} = {1}", e.ToString(), (byte)e).Dump();
}

Ergebnisse in:

None = 0
One = 1
Two = 2
OneAndTwo = 3
Three = 4
OneTwoAndThree = 7
Four = 8

86
2018-05-16 15:03



Im Folgenden finden Sie ein Beispiel, das die Deklaration und mögliche Verwendung zeigt:

namespace Flags
{
    class Program
    {
        [Flags]
        public enum MyFlags : short
        {
            Foo = 0x1,
            Bar = 0x2,
            Baz = 0x4
        }

        static void Main(string[] args)
        {
            MyFlags fooBar = MyFlags.Foo | MyFlags.Bar;

            if ((fooBar & MyFlags.Foo) == MyFlags.Foo)
            {
                Console.WriteLine("Item has Foo flag set");
            }
        }
    }
}

44
2017-08-12 04:32



ich fragte kürzlich über etwas Ähnliches.

Wenn Sie Flags verwenden, können Sie enums eine Erweiterungsmethode hinzufügen, um das Überprüfen der enthaltenen Flags zu vereinfachen (siehe Beitrag für Details).

Dies ermöglicht Ihnen Folgendes:

[Flags]
public enum PossibleOptions : byte
{
    None = 0,
    OptionOne = 1,
    OptionTwo = 2,
    OptionThree = 4,
    OptionFour = 8,

    //combinations can be in the enum too
    OptionOneAndTwo = OptionOne | OptionTwo,
    OptionOneTwoAndThree = OptionOne | OptionTwo | OptionThree,
    ...
}

Dann können Sie tun:

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

if( opt.IsSet( PossibleOptions.OptionOne ) ) {
    //optionOne is one of those set
}

Ich finde das leichter zu lesen als die meisten Möglichkeiten, die enthaltenen Flaggen zu überprüfen.


30
2017-08-12 18:40



@Nidonocu

Verwenden Sie den OR-Zuweisungsoperator, um einer vorhandenen Gruppe von Werten ein weiteres Flag hinzuzufügen.

Mode = Mode.Read;
//Add Mode.Write
Mode |= Mode.Write;
Assert.True(((Mode & Mode.Write) == Mode.Write)
  && ((Mode & Mode.Read) == Mode.Read)));

19
2017-08-12 15:37



In Erweiterung zu der angenommenen Antwort können in C # 7 die Enum-Flags mit binären Literalen geschrieben werden:

[Flags]
public enum MyColors
{
    None   = 0b0000,
    Yellow = 0b0001,
    Green  = 0b0010,
    Red    = 0b0100,
    Blue   = 0b1000
}

Ich denke, diese Darstellung macht deutlich, wie die Flaggen funktionieren unter der Decke.


16
2017-07-20 11:09