Se ho una variabile che contiene un enumerazione di flag, posso in qualche modo scorrere i valori di bit in quella specifica variabile? O devo usare Enum.GetValues per iterare sull'intero enum e controllare quali sono impostati?Iterate sui valori in Flags Enum?
risposta
Tornando a questo un paio di anni più tardi, con un po 'più di esperienza, la mia risposta definitiva per solo valori singoli bit, passando dal bit più basso al più alto bit, è una leggera variante di Jeff Mercado interna di routine:
public static IEnumerable<Enum> GetUniqueFlags(this Enum flags)
{
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<Enum>())
{
ulong bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value))
{
yield return value;
}
}
}
sembra funzionare, e nonostante le mie obiezioni di alcuni anni fa, io uso hasFlag qui, dal momento che è molto più leggibile rispetto all'utilizzo di confronto bit per bit e la differenza di velocità è insignificante per qualsiasi cosa farò. (È del tutto possibile che abbiano migliorato la velocità di HasFlags da allora comunque, per quel che ne so ... non ho provato.)
Solo un flag di nota è inizializzato ad un int dovrebbe essere ad un ulong come bit, dovrebbe essere inizializzato come * 1ul * – forcewill
Grazie, lo aggiusterò! (Sono appena andato a controllare il mio codice reale e l'ho già risolto in senso contrario dichiarando espressamente che è un ulong.) – RobinHood70
Questa è l'unica soluzione che ho trovato che sembra anche non soffrire del fatto che se tu avere un flag con valore zero, che dovrebbe rappresentare "None", altre risposte I metodi GetFlag() restituiranno YourEnum.None come uno dei flag anche se non si trova effettivamente nell'enum su cui si sta eseguendo il metodo! Avevo delle strane voci di registro duplicate perché i metodi venivano eseguiti più volte di quanto mi aspettassi quando avevano un solo set di enum non zero. Grazie per aver dedicato del tempo per aggiornare e aggiungere questa grande soluzione! – BrianHall
Non ci sono metodi AFAIK per ottenere ogni componente. Ecco un modo si possono ottenere:
[Flags]
enum Items
{
None = 0x0,
Foo = 0x1,
Bar = 0x2,
Baz = 0x4,
Boo = 0x6,
}
var value = Items.Foo | Items.Bar;
var values = value.ToString()
.Split(new[] { ", " }, StringSplitOptions.None)
.Select(v => (Items)Enum.Parse(typeof(Items), v));
// This method will always end up with the most applicable values
value = Items.Bar | Items.Baz;
values = value.ToString()
.Split(new[] { ", " }, StringSplitOptions.None)
.Select(v => (Items)Enum.Parse(typeof(Items), v)); // Boo
Ho adattato quello Enum
fa internamente per generare la stringa di restituire invece le bandiere. Puoi guardare il codice nel riflettore e dovrebbe essere più o meno equivalente. Funziona bene per i casi di uso generale dove ci sono valori che contengono più bit.
static class EnumExtensions
{
public static IEnumerable<Enum> GetFlags(this Enum value)
{
return GetFlags(value, Enum.GetValues(value.GetType()).Cast<Enum>().ToArray());
}
public static IEnumerable<Enum> GetIndividualFlags(this Enum value)
{
return GetFlags(value, GetFlagValues(value.GetType()).ToArray());
}
private static IEnumerable<Enum> GetFlags(Enum value, Enum[] values)
{
ulong bits = Convert.ToUInt64(value);
List<Enum> results = new List<Enum>();
for (int i = values.Length - 1; i >= 0; i--)
{
ulong mask = Convert.ToUInt64(values[i]);
if (i == 0 && mask == 0L)
break;
if ((bits & mask) == mask)
{
results.Add(values[i]);
bits -= mask;
}
}
if (bits != 0L)
return Enumerable.Empty<Enum>();
if (Convert.ToUInt64(value) != 0L)
return results.Reverse<Enum>();
if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L)
return values.Take(1);
return Enumerable.Empty<Enum>();
}
private static IEnumerable<Enum> GetFlagValues(Type enumType)
{
ulong flag = 0x1;
foreach (var value in Enum.GetValues(enumType).Cast<Enum>())
{
ulong bits = Convert.ToUInt64(value);
if (bits == 0L)
//yield return value;
continue; // skip the zero value
while (flag < bits) flag <<= 1;
if (flag == bits)
yield return value;
}
}
}
Il metodo di estensione GetIndividualFlags()
ottiene tutti i singoli bandiere per un tipo. Quindi i valori contenenti più bit sono esclusi.
var value = Items.Bar | Items.Baz;
value.GetFlags(); // Boo
value.GetIndividualFlags(); // Bar, Baz
Avevo preso in considerazione l'idea di eseguire uno split split, ma questo probabilmente è un overhead molto più che semplicemente l'iterazione dei valori bit dell'intero enum. – RobinHood70
Sfortunatamente così facendo, dovresti testare i valori ridondanti (se non li volessi). Vedi il mio secondo esempio, produrrebbe 'Bar',' Baz' e 'Boo' invece di solo' Boo'. –
Interessante che tu riesca a far uscire Boo da quello, anche se quella parte non è necessaria (e in effetti, una pessima idea :)) per quello che sto facendo. – RobinHood70
Non è necessario ripetere tutti i valori. basta controllare i flag specifici in questo modo:
if((myVar & FlagsEnum.Flag1) == FlagsEnum.Flag1)
{
//do something...
}
o (come detto pstrjds nei commenti), è possibile verificare la presenza di utilizzarlo come:
if(myVar.HasFlag(FlagsEnum.Flag1))
{
//do something...
}
Se stai usando .Net 4.0 c'è un metodo di estensione HasFlag che puoi usare per fare la stessa cosa: myVar.HasFlag (FlagsEnum.Flag1) – pstrjds
grazie ... non ho mai visto quello che dici, ma dovrebbe essere così bravo ... :) –
Se un programmatore non riesce a capire un'operazione AND bit a bit, dovrebbe comprimerlo e trovare una nuova carriera. –
È possibile utilizzare un Iterator dal Enum. A partire dal codice MSDN:
public class DaysOfTheWeek : System.Collections.IEnumerable
{
int[] dayflag = { 1, 2, 4, 8, 16, 32, 64 };
string[] days = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
public string value { get; set; }
public System.Collections.IEnumerator GetEnumerator()
{
for (int i = 0; i < days.Length; i++)
{
if value >> i & 1 == dayflag[i] {
yield return days[i];
}
}
}
}
Non è testato, quindi se ho fatto un errore non esitate a chiamarmi. (ovviamente non è ri-entrante.) Dovresti assegnare un valore in anticipo, o scomporlo in un'altra funzione che usa enum.dayflag ed enum.days. Potresti essere in grado di andare da qualche parte con il contorno.
static IEnumerable<Enum> GetFlags(Enum input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
if (input.HasFlag(value))
yield return value;
}
Questo è vicino a quello che sto facendo, anche se non avevo pensato di costruire il mio enumeratore (o noto che potevo). Sono ancora abbastanza nuovo per C#, quindi questa è stata sicuramente una risposta educativa per me. Grazie! – RobinHood70
Si noti che 'HasFlag' è disponibile da .NET 4 in poi. –
Questo è fantastico! Ma puoi renderlo ancora più semplice e facile da usare. Solo bastone questo come un metodo di estensione: 'Enum.GetValues (input.GetType()) Cast
Non soddisfatto delle risposte di cui sopra, sebbene fossero l'inizio.
Dopo mettendo insieme alcune fonti differenti qui:
Previous poster in this thread's SO QnA
Code Project Enum Flags Check Post
Great Enum<T> Utility
Ho creato questo modo fatemi sapere cosa ne pensate.
Parametri:
bool checkZero
: indica di consentire 0
come valore di contrassegno. Per impostazione predefinita, input = 0
restituisce vuoto.
bool checkFlags
: indica per verificare se lo Enum
è decorato con l'attributo [Flags]
.
PS. Non ho tempo ora per capire l'alg checkCombinators = false
che lo costringerà a ignorare tutti i valori enum che sono combinazioni di bit.
public static IEnumerable<TEnum> GetFlags<TEnum>(this TEnum input, bool checkZero = false, bool checkFlags = true, bool checkCombinators = true)
{
Type enumType = typeof(TEnum);
if (!enumType.IsEnum)
yield break;
ulong setBits = Convert.ToUInt64(input);
// if no flags are set, return empty
if (!checkZero && (0 == setBits))
yield break;
// if it's not a flag enum, return empty
if (checkFlags && !input.GetType().IsDefined(typeof(FlagsAttribute), false))
yield break;
if (checkCombinators)
{
// check each enum value mask if it is in input bits
foreach (TEnum value in Enum<TEnum>.GetValues())
{
ulong valMask = Convert.ToUInt64(value);
if ((setBits & valMask) == valMask)
yield return value;
}
}
else
{
// check each enum value mask if it is in input bits
foreach (TEnum value in Enum <TEnum>.GetValues())
{
ulong valMask = Convert.ToUInt64(value);
if ((setBits & valMask) == valMask)
yield return value;
}
}
}
Questo fa uso della classe di supporto Enum<T> found here che ho aggiornato da usare yield return
per GetValues
:
public static class Enum<TEnum>
{
public static TEnum Parse(string value)
{
return (TEnum)Enum.Parse(typeof(TEnum), value);
}
public static IEnumerable<TEnum> GetValues()
{
foreach (object value in Enum.GetValues(typeof(TEnum)))
yield return ((TEnum)value);
}
}
Infine, ecco un esempio di utilizzo di esso:
private List<CountType> GetCountTypes(CountType countTypes)
{
List<CountType> cts = new List<CountType>();
foreach (var ct in countTypes.GetFlags())
cts.Add(ct);
return cts;
}
Mi spiace, non ho avuto il tempo di guardare questo progetto tra diversi giorni. Ti risponderò una volta che avrò dato un'occhiata al tuo codice. – RobinHood70
Solo un avviso che c'è un bug in quel codice. Il codice in entrambi i rami dell'istruzione if (checkCombinators) è identico. Inoltre, forse non un bug, ma inaspettato, è che se si ha un valore enum dichiarato per 0, verrà sempre restituito nella raccolta. Sembra che dovrebbe essere restituito solo se checkZero è true e non ci sono altri flag impostati. – dhochee
@dhochee. Sono d'accordo. O il codice è piuttosto carino, ma gli argomenti sono confusi. – AFract
Quello che ho fatto è stato cambiare il mio approccio, invece di digitare il parametro di input del metodo come il tipo enum
, l'ho digitato come un array del enum
tipo (MyEnum[] myEnums
), in questo modo ho solo scorrere l'array con un'istruzione switch all'interno del ciclo.
Basandosi sulla risposta di Greg sopra, questo si occupa anche del caso in cui si ha un valore 0 nel proprio enum, come None = 0. In tal caso, non dovrebbe iterare su quel valore.
public static IEnumerable<Enum> ToEnumerable(this Enum input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
if (input.HasFlag(value) && Convert.ToInt64(value) != 0)
yield return value;
}
Qualcuno sapere come migliorare su questo ancora di più in modo che possa gestire il caso in cui sono fissati in un modo super intelligente in grado di gestire tutti i sottostanti tipo enum e il caso di tutti i = tutte le bandiere del enum ~ 0 e Tutti = EnumValue1 | EnumValue2 | EnumValue3 | ...
Ecco una soluzione Linq al problema.
public static IEnumerable<Enum> GetFlags(this Enum e)
{
return Enum.GetValues(e.GetType()).Cast<Enum>().Where(e.HasFlag);
}
Perché non è questo in cima ?? :) Usa '.Where (v =>! Equals ((int) (oggetto) v, 0) && e.HasFlag (v));' se hai un valore zero per rappresentare 'None' – georgiosd
Puoi farlo direttamente convertendo in int ma perderai il controllo del tipo. Penso che il modo migliore sia usare qualcosa di simile alla mia proposta. Mantiene il tipo corretto fino in fondo. Nessuna conversione richiesta. Non è perfetto a causa del pugilato che aggiungerà un piccolo successo in termini di prestazioni.
Non perfetta (pugilato), ma fa il lavoro senza preavviso ...
/// <summary>
/// Return an enumerators of input flag(s)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static IEnumerable<T> GetFlags<T>(this T input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
{
if ((int) (object) value != 0) // Just in case somebody has defined an enum with 0.
{
if (((Enum) (object) input).HasFlag(value))
yield return (T) (object) value;
}
}
}
Usage:
FileAttributes att = FileAttributes.Normal | FileAttributes.Compressed;
foreach (FileAttributes fa in att.GetFlags())
{
...
}
+1 per la risposta fornita da @ RobinHood70. Ho trovato che una versione generica del metodo era conveniente per me.
public static IEnumerable<T> GetUniqueFlags<T>(this Enum flags)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("The generic type parameter must be an Enum.");
if (flags.GetType() != typeof(T))
throw new ArgumentException("The generic type parameter does not match the target type.");
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<T>())
{
ulong bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value as Enum))
{
yield return value;
}
}
}
- 1. Attributo Flags Enum
- 2. Tipo bit sicuro enum flags
- 3. Iterate over enum?
- 4. Iterate over direction enum
- 5. Selezione MySQL basata sui valori ENUM
- 6. Proprietà controllo ASP.NET con [Flags] enum
- 7. Valori dispari enum in Windows.Forms.MouseButtons
- 8. Equivalente di C# Enum Flags Attribute in Java?
- 9. Accendi Enum (con attributo Flags) senza dichiarare ogni combinazione possibile?
- 10. C# Enums con l'attributo Flags
- 11. iterate valori int statici in java
- 12. Più modi per definire C# Enum con l'attributo [Flags]?
- 13. Enum.TryParse con attributo Flags
- 14. C#, Flags Enum, Funzione generica per cercare una bandiera
- 15. iterate attraverso elementi enum non consecutivi in C++
- 16. Enum, valori sovrapposti, C#
- 17. looping dei valori enum
- 18. come confrontare valori enum
- 19. Confronta valori enum Java
- 20. Mysql Seleziona valori enum
- 21. EnumMap vs. Enum valori
- 22. Riutilizzo dei valori enum in tipi di enum separati
- 23. Come abilitare google chrome chrome: // flags/valori utilizzando javascript?
- 24. Ciclo attraverso i valori Enum "selezionati"?
- 25. virgolette sui valori int in query mysql
- 26. Elenco dei valori enum in java
- 27. Accesso valori di enum in Rust
- 28. enum di valori non letterali in Swift
- 29. Come evitare i valori duplicati in enum?
- 30. Assegnazione errata dei valori in enum
Se si ha il controllo sulla propria API, evitare l'uso di flag di bit. Raramente sono un'ottimizzazione utile. L'utilizzo di una struct con diversi campi "bool" pubblici è semanticamente equivalente ma il tuo codice è molto più semplice. E se è necessario in seguito, è possibile modificare i campi in proprietà che manipolano internamente i campi di bit, incapsulando l'ottimizzazione. –
Vedo quello che stai dicendo, e in molti casi avrebbe senso, ma in questo caso, avrei lo stesso problema del suggerimento If ...Dovrei scrivere istruzioni If per una dozzina di bool diversi invece di usare un semplice ciclo foreach su un array. (E poiché questo fa parte di una DLL pubblica, non posso fare cose arcane come avere una serie di bool o cosa hai.) – RobinHood70
possibile duplicato di [Metodo di estensione generico per vedere se un enum contiene una bandiera] (http : //stackoverflow.com/questions/4108828/generic-extension-method-to-see-if-an-enum-taintain-a-flag) – nawfal