Co atrybut [Flags] Enum oznacza w C#?
Od czasu do czasu widzę enum jak poniżej:
[Flags]
public enum Options
{
None = 0,
Option1 = 1,
Option2 = 2,
Option3 = 4,
Option4 = 8
}
Nie rozumiem, co dokładnie robi [Flags]
-atrybut.
Ktoś ma dobre wyjaśnienie lub przykład, który mógłby zamieścić?
10 answers
Atrybut flags powinien być używany, gdy enumerable reprezentuje zbiór flag, a nie pojedynczą wartość. Takie zbiory są zwykle manipulowane za pomocą operatorów bitowych, na przykład:
myProperties.AllowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;
Zauważ, że [Flags]
samo w sobie nie zmienia tego w ogóle - wszystko, co robi, to umożliwia ładną reprezentację metodą .ToString()
:
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"
Ważne jest również, aby pamiętać, że [Flags]
nie powoduje, że automatycznie powoduje, że wartości enum są potęgami dwóch. Jeśli pomijając wartości liczbowe, enum nie będzie działać tak, jak można się spodziewać w operacjach bitowych, ponieważ domyślnie wartości zaczynają się od 0 i przyrostu.
Niepoprawna deklaracja:
[Flags]
public enum MyColors
{
Yellow,
Green,
Red,
Blue
}
Wartości, jeśli zadeklarowane w ten sposób, będą żółte = 0, Zielone = 1, Czerwone = 2, niebieskie = 3. To uczyni go bezużytecznym do użycia jako flagi.
Oto przykład poprawnej deklaracji:
[Flags]
public enum MyColors
{
Yellow = 1,
Green = 2,
Red = 4,
Blue = 8
}
Aby pobrać różne wartości w swojej właściwości, można zrobić to:
if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
{
// Yellow has been set...
}
if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
{
// Green has been set...
}
[[13]} lub, w. Net 4 i później:
if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
{
// Yellow has been set...
}
Pod pokrywami
To działa, ponieważ wcześniej użyłeś mocy dwóch w swoim wyliczeniu. Pod pokrywami wartości wyliczania wyglądają tak (przedstawione jako bajty, które mają 8 bitów, które mogą być 1 lub 0)
Yellow: 00000001
Green: 00000010
Red: 00000100
Blue: 00001000
Podobnie, Po ustawieniu właściwości AllowedColors na czerwony, zielony i Niebieski (które wartości gdzie lub ' wstawione przez rurę |), AllowedColors wygląda jak to
myProperties.AllowedColors: 00001110
Więc kiedy pobierasz wartość, jesteś w rzeczywistości bitowy i zmieniasz wartości
myProperties.AllowedColors: 00001110
MyColor.Green: 00000010
-----------------------
00000010 // Hey, this is the same as MyColor.Green!
Wartość None = 0
I jeśli chodzi o użycie 0 w wyliczeniu, cytując z msdn:
[Flags]
public enum MyColors
{
None = 0,
....
}
Więcej informacji na temat atrybutu flags i jego użycia można znaleźć na stronie msdn oraz projektowanie FLAG na stronie msdnUżyj None jako nazwy znacznika wyliczonej stałej, której wartość wynosi zero. nie można użyć stałej None w operacji bitowej i do sprawdzenia znacznika, ponieważ wynik jest zawsze zerowy. można jednak wykonać logiczne, nie jest to bitowe porównanie wartości liczbowej ze stałą wyliczoną None, aby określić, czy jakiekolwiek bity w wartości liczbowej są ustawione.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-28 00:12:02
Możesz również to zrobić
[Flags]
public enum MyEnum
{
None = 0,
First = 1 << 0,
Second = 1 << 1,
Third = 1 << 2,
Fourth = 1 << 3
}
Uważam, że zmiana bitów jest łatwiejsza niż wpisanie 4,8,16,32 i tak dalej. Nie ma to wpływu na Twój kod, ponieważ wszystko odbywa się w czasie kompilacji
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 02:45:27
Łączenie odpowiedzi https://stackoverflow.com/a/8462/1037948 (deklaracja poprzez przesuwanie bitów) i https://stackoverflow.com/a/9117/1037948 (używając kombinacji w deklaracji) możesz przesuwać bitowo poprzednie wartości zamiast używać liczb. Niekoniecznie polecając, ale po prostu wskazując, że możesz.
Zamiast:
[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,
}
Możesz zadeklarować
[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,
}
Potwierdzenie za pomocą LinqPad:
foreach(var e in Enum.GetValues(typeof(Options))) {
string.Format("{0} = {1}", e.ToString(), (byte)e).Dump();
}
Wyniki w:
None = 0
One = 1
Two = 2
OneAndTwo = 3
Three = 4
OneTwoAndThree = 7
Four = 8
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:37
Zobacz poniższy przykład, który pokazuje deklarację i potencjalne użycie:
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");
}
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-14 02:23:29
Zapytałam ostatnioo coś podobnego.
Jeśli używasz FLAG, możesz dodać metodę rozszerzenia do enum, aby ułatwić sprawdzanie zawartych FLAG (szczegóły w poście)
Pozwala to na:
[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,
...
}
Wtedy możesz zrobić:
PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree
if( opt.IsSet( PossibleOptions.OptionOne ) ) {
//optionOne is one of those set
}
Uważam, że jest to łatwiejsze do odczytania niż większość sposobów sprawdzania dołączonych FLAG.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:54
@ Nidonocu
Aby dodać kolejny znacznik do istniejącego zbioru wartości, użyj operatora przypisania OR.
Mode = Mode.Read;
//Add Mode.Write
Mode |= Mode.Write;
Assert.True(((Mode & Mode.Write) == Mode.Write)
&& ((Mode & Mode.Read) == Mode.Read)));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-12 15:37:42
W rozszerzeniu do zaakceptowanej odpowiedzi, w C#7 znaczniki enum mogą być zapisywane za pomocą literałów binarnych:
[Flags]
public enum MyColors
{
None = 0b0000,
Yellow = 0b0001,
Green = 0b0010,
Red = 0b0100,
Blue = 0b1000
}
Myślę, że ta reprezentacja wyjaśnia, jak działają flagi pod okładkami .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-20 11:09:19
Aby dodać Mode.Write
:
Mode = Mode | Mode.Write;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-14 05:46:00
Jest dla mnie coś zbyt gadatliwego o konstrukcji if ((x & y) == y)...
, zwłaszcza jeśli x
I y
są złożonymi zbiorami FLAG i chcesz wiedzieć tylko, czy istnieje jakiekolwiek nakładanie się.
W tym przypadku, wszystko, co naprawdę musisz wiedzieć, to czy istnieje niezerowa wartość[1] po bitmasked.
[1] Zobacz komentarz Jaime ' a. Gdybyśmy byli autentycznie bitmasking, to wystarczy sprawdzić, czy wynik był pozytywny. Ale od
enum
s can być ujemne, parzyste, o dziwo, w połączeniu z[Flags]
atrybut , defensywne jest kodowanie!= 0
zamiast> 0
.
Budowanie z konfiguracji @ andnil...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BitFlagPlay
{
class Program
{
[Flags]
public enum MyColor
{
Yellow = 0x01,
Green = 0x02,
Red = 0x04,
Blue = 0x08
}
static void Main(string[] args)
{
var myColor = MyColor.Yellow | MyColor.Blue;
var acceptableColors = MyColor.Yellow | MyColor.Red;
Console.WriteLine((myColor & MyColor.Blue) != 0); // True
Console.WriteLine((myColor & MyColor.Red) != 0); // False
Console.WriteLine((myColor & acceptableColors) != 0); // True
// ... though only Yellow is shared.
Console.WriteLine((myColor & MyColor.Green) != 0); // Wait a minute... ;^D
Console.Read();
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-16 13:40:31
Flagi pozwalają na użycie bitmaskingu wewnątrz wyliczenia. Pozwala to na łączenie wartości wyliczania, zachowując przy tym, które z nich są określone.
[Flags]
public enum DashboardItemPresentationProperties : long
{
None = 0,
HideCollapse = 1,
HideDelete = 2,
HideEdit = 4,
HideOpenInNewWindow = 8,
HideResetSource = 16,
HideMenu = 32
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-07 18:21:45