Cosa significa l’attributo Enum in C #?

Di tanto in tanto vedo una enumerazione come la seguente:

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

Non capisco cosa faccia esattamente l’attributo [Flags] .

Qualcuno ha una buona spiegazione o un esempio da pubblicare?

L’attributo flags deve essere utilizzato ogni volta che l’enumerable rappresenta una raccolta di flag, piuttosto che un singolo valore. Tali raccolte vengono solitamente manipolate utilizzando operatori bit a bit, ad esempio:

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

Nota che [Flags] per sé non cambia tutto – tutto ciò che fa è abilitare una bella rappresentazione con il metodo .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" 

È anche importante notare che [Flags] non rende automaticamente i poteri enum di due. Se si omettono i valori numerici, l’enumerazione non funzionerà come previsto nelle operazioni bit a bit, poiché per impostazione predefinita i valori iniziano con 0 e incrementano.

Dichiarazione errata:

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

I valori, se dichiarati in questo modo, saranno Giallo = 0, Verde = 1, Rosso = 2, Blu = 3. Questo lo renderà inutilizzabile per l’uso come bandiere.

Ecco un esempio di una dichiarazione corretta:

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

Per recuperare i valori distinti nella tua proprietà, si può fare questo:

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

oppure, in .NET 4 e versioni successive:

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

Sotto le coperte

Funziona perché in precedenza hai usato i poteri di due nella tua enumerazione. Sotto le copertine, i tuoi valori di enumerazione assomigliano a questo (presentato come byte, che ha 8 bit che possono essere 1 o 0)

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

Allo stesso modo, dopo aver impostato la proprietà AllowedColors su Red, Green e Blue (che valori dove OR’ed dal pipe |), AllowedColors assomiglia a questo

 myProperties.AllowedColors: 00001110 

Quindi, quando recuperi il valore, in realtà stai utilizzando i valori AND bit a bit

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

Il valore None = 0

E riguardo all’uso 0 nella tua enumerazione, citando da msdn:

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

Utilizzare None come nome della costante enumerata flag il cui valore è zero. Non è ansible utilizzare la costante enumerata None in un’operazione AND bit per verificare un flag perché il risultato è sempre zero. Tuttavia, è ansible eseguire un confronto logico, non a un bit, tra il valore numerico e la costante enumerata None per determinare se sono impostati bit nel valore numerico.

Puoi trovare maggiori informazioni sull’attributo flags e il suo utilizzo su msdn e sulla progettazione dei flag su msdn

Puoi anche farlo

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

Trovo che lo spostamento del bit sia più semplice della digitazione 4,8,16,32 e così via. Non ha alcun impatto sul tuo codice perché è tutto fatto in fase di compilazione

Combinando le risposte https://stackoverflow.com/a/8462/1037948 (dichiarazione tramite bit-shifting) e https://stackoverflow.com/a/9117/1037948 (usando le combinazioni in dichiarazione) è ansible spostare i valori precedenti in bit che usare i numeri. Non necessariamente raccomandarlo, ma solo sottolineando che puoi.

Piuttosto che:

 [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, } 

Puoi dichiarare

 [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, } 

Conferma con LinqPad:

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

Risultati in:

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

Si prega di consultare quanto segue per un esempio che mostra la dichiarazione e l’utilizzo potenziale:

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

Ho chiesto di recente qualcosa di simile.

Se usi i flag puoi aggiungere un metodo di estensione alle enumerazioni per rendere più semplice il controllo dei flag contenuti (vedi post per i dettagli)

Questo ti permette di fare:

 [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, ... } 

Quindi puoi fare:

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

Trovo questo più facile da leggere rispetto al maggior numero di modi di controllare le bandiere incluse.

@Nidonocu

Per aggiungere un altro flag a un set di valori esistente, utilizzare l’operatore di assegnazione OR.

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

In estensione alla risposta accettata, in C # 7 i flag enum possono essere scritti usando i valori binari letterali:

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

Penso che questa rappresentazione chiarisca come funzionano le bandiere sotto le coperte .

Per aggiungere Mode.Write :

 Mode = Mode | Mode.Write; 

C’è qualcosa di eccessivamente verboso per me sul costrutto if ((x & y) == y)... , specialmente se x AND y sono entrambi insiemi composti di flag e vuoi solo sapere se c’è qualche sovrapposizione.

In questo caso, tutto ciò che devi veramente sapere è se c’è un valore diverso da zero [1] dopo che hai mascherato il bit .

[1] Vedi il commento di Jaime. Se fossimo autenticamente bitmasking , avremmo solo bisogno di verificare che il risultato fosse positivo. Ma dato che enum s può essere negativo, anche, stranamente, quando combinato con l’attributo [Flags] , è difensivo per il codice per != 0 piuttosto che > 0 .

Compilare l’impostazione di @ 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(); } } } 

I flag consentono di utilizzare il bitmasking all’interno dell’enumerazione. Ciò consente di combinare i valori di enumerazione, pur mantenendo quelli specificati.

 [Flags] public enum DashboardItemPresentationProperties : long { None = 0, HideCollapse = 1, HideDelete = 2, HideEdit = 4, HideOpenInNewWindow = 8, HideResetSource = 16, HideMenu = 32 }