Iteramos los valores en Flags Enum?

Si tengo una variable que contenga una lista de banderas, ¿puedo de alguna manera iterar sobre los valores de los bits en esa variable específica? ¿O tengo que usar Enum.GetValues ​​para iterar sobre toda la enumeración y verificar cuáles están configurados?

static IEnumerable GetFlags(Enum input) { foreach (Enum value in Enum.GetValues(input.GetType())) if (input.HasFlag(value)) yield return value; } 

No hay ningún método AFAIK para obtener cada componente. Aquí hay una forma de obtenerlos:

 [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 

He adaptado lo que Enum hace internamente para generar la cadena para devolver las banderas. Puedes mirar el código en el reflector y debería ser más o menos equivalente. Funciona bien para casos de uso general donde hay valores que contienen múltiples bits.

 static class EnumExtensions { public static IEnumerable GetFlags(this Enum value) { return GetFlags(value, Enum.GetValues(value.GetType()).Cast().ToArray()); } public static IEnumerable GetIndividualFlags(this Enum value) { return GetFlags(value, GetFlagValues(value.GetType()).ToArray()); } private static IEnumerable GetFlags(Enum value, Enum[] values) { ulong bits = Convert.ToUInt64(value); List results = new List(); 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(); if (Convert.ToUInt64(value) != 0L) return results.Reverse(); if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L) return values.Take(1); return Enumerable.Empty(); } private static IEnumerable GetFlagValues(Type enumType) { ulong flag = 0x1; foreach (var value in Enum.GetValues(enumType).Cast()) { 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; } } } 

El método de extensión GetIndividualFlags() obtiene todos los indicadores individuales para un tipo. Por lo tanto, los valores que contienen múltiples bits quedan fuera.

 var value = Items.Bar | Items.Baz; value.GetFlags(); // Boo value.GetIndividualFlags(); // Bar, Baz 

Aquí hay una solución de Linq al problema.

 public static IEnumerable GetFlags(this Enum e) { return Enum.GetValues(e.GetType()).Cast().Where(e.HasFlag); } 

Volviendo a esto unos años más tarde, con un poco más de experiencia, mi última respuesta para los valores de un solo bit, pasando del bit más bajo al bit más alto, es una ligera variante de la rutina interna de Jeff Mercado:

 public static IEnumerable GetUniqueFlags(this Enum flags) { ulong flag = 1; foreach (var value in Enum.GetValues(flags.GetType()).Cast()) { ulong bits = Convert.ToUInt64(value); while (flag < bits) { flag <<= 1; } if (flag == bits && flags.HasFlag(value)) { yield return value; } } } 

Parece que funciona, y a pesar de mis objeciones de hace algunos años, uso HasFlag aquí, ya que es mucho más legible que el uso de comparaciones bit a bit y la diferencia de velocidad es insignificante para cualquier cosa que vaya a hacer. (Es muy posible que hayan mejorado la velocidad de HasFlags desde entonces, de todos modos, por lo que sé ... no lo he probado).

+1 a la respuesta proporcionada por @ RobinHood70. Descubrí que una versión genérica del método me resultaba conveniente.

 public static IEnumerable GetUniqueFlags(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()) { ulong bits = Convert.ToUInt64(value); while (flag < bits) { flag <<= 1; } if (flag == bits && flags.HasFlag(value as Enum)) { yield return value; } } } 

Saliendo del método de @ Greg, pero agregando una nueva característica de C # 7.3, la restricción Enum :

 public static IEnumerable GetUniqueFlags(this Enum flags) where T : Enum // New constraint for C# 7.3 { foreach (Enum value in Enum.GetValues(flags.GetType())) if (flags.HasFlag(value)) yield return (T)value; } 

La nueva restricción permite que esto sea un método de extensión, sin tener que HasFlag a través de (int)(object)e , y puedo usar el método HasFlag y convertirlo directamente a T desde el value .

C # 7.3 también agregó restricciones para desglosados ​​y unmanaged .

No estaba satisfecho con las respuestas anteriores, aunque fueron el comienzo.

Después de unir algunas fonts diferentes aquí:
Poster anterior en SO QnA de este hilo
Código del proyecto Enum Flags Check Post
Gran Enum Utilidad

Creé esto, así que déjame saber lo que piensas.
Parámetros:
bool checkZero : le dice que permita 0 como valor de indicador. Por defecto input = 0 devuelve empty.
bool checkFlags : le dice que verifique si el Enum está decorado con el atributo [Flags] .
PD. No tengo tiempo ahora para descubrir el checkCombinators = false alg que lo forzará a ignorar cualquier valor enum que sea una combinación de bits.

  public static IEnumerable GetFlags(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.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 .GetValues()) { ulong valMask = Convert.ToUInt64(value); if ((setBits & valMask) == valMask) yield return value; } } } 

Esto hace uso de Helper Class Enum que se encuentra aquí y que actualicé para usar el yield return para GetValues :

 public static class Enum { public static TEnum Parse(string value) { return (TEnum)Enum.Parse(typeof(TEnum), value); } public static IEnumerable GetValues() { foreach (object value in Enum.GetValues(typeof(TEnum))) yield return ((TEnum)value); } } 

Finalmente, aquí hay un ejemplo de cómo usarlo:

  private List GetCountTypes(CountType countTypes) { List cts = new List(); foreach (var ct in countTypes.GetFlags()) cts.Add(ct); return cts; } 

No necesita iterar todos los valores. simplemente revise sus banderas específicas de esta manera:

 if((myVar & FlagsEnum.Flag1) == FlagsEnum.Flag1) { //do something... } 

o (como dijo pstrjds en los comentarios) puede verificar si lo usa como:

 if(myVar.HasFlag(FlagsEnum.Flag1)) { //do something... } 

Lo que hice fue cambiar mi enfoque, en lugar de escribir el parámetro de entrada del método como el tipo enum , lo escribí como una matriz del tipo de enum ( MyEnum[] myEnums ), de esta manera simplemente itere a través de la matriz con un interruptor statement dentro del ciclo.

Sobre la base de la respuesta de Greg anterior, esto también se ocupa del caso en el que tiene un valor 0 en su enumeración, como None = 0. En cuyo caso, no debe iterar sobre ese valor.

 public static IEnumerable ToEnumerable(this Enum input) { foreach (Enum value in Enum.GetValues(input.GetType())) if (input.HasFlag(value) && Convert.ToInt64(value) != 0) yield return value; } 

¿Alguien sabría cómo mejorar esto aún más para que pueda manejar el caso donde todos los indicadores en la enumeración se configuran de una manera súper inteligente que podría manejar todo el tipo de enumeración subyacente y el caso de All = ~ 0 y All = EnumValue1 | EnumValue2 | EnumValue3 | …

Puede usar un Iterador de Enum. A partir del código de 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]; } } } } 

No está probado, así que si cometí un error, no dude en llamarme. (obviamente no es reentrante). Tendría que asignar valor de antemano o dividirlo en otra función que use enum.dayflag y enum.days. Es posible que pueda ir a algún lado con el esquema.

Podría ser así como el siguiente código:

 public static string GetEnumString(MyEnum inEnumValue) { StringBuilder sb = new StringBuilder(); foreach (MyEnum e in Enum.GetValues(typeof(MyEnum ))) { if ((e & inEnumValue) != 0) { sb.Append(e.ToString()); sb.Append(", "); } } return sb.ToString().Trim().TrimEnd(','); } 

Entra si solo cuando el valor enum está contenido en el valor

Puede hacerlo directamente convirtiendo a int, pero perderá la verificación de tipo. Creo que la mejor manera es usar algo similar a mi proposición. Mantiene el tipo correcto todo el camino. No se requiere conversión No es perfecto debido al boxeo, que agregará un poco de éxito en el rendimiento.

No es perfecto (boxeo), pero cumple su función sin previo aviso …

 ///  /// Return an enumerators of input flag(s) ///  ///  ///  public static IEnumerable GetFlags(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; } } } 

Uso:

  FileAttributes att = FileAttributes.Normal | FileAttributes.Compressed; foreach (FileAttributes fa in att.GetFlags()) { ... }