switch - enum in model c#
Iteramos los valores en Flags Enum? (13)
+1 a la respuesta proporcionada por @ RobinHood70. Descubrí que una versión genérica del método me resultaba conveniente.
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;
}
}
}
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?
Aquí hay una solución de Linq al problema.
public static IEnumerable<Enum> GetFlags(this Enum e)
{
return Enum.GetValues(e.GetType()).Cast<Enum>().Where(e.HasFlag);
}
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 declaración dentro del ciclo.
No estaba satisfecho con las respuestas anteriores, aunque fueron el comienzo.
Después de unir algunas fuentes diferentes aquí:
Poster anterior en SO QnA de este hilo
Código del proyecto Enum Flags Check Post
Gran Enum <T> 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<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;
}
}
}
Esto hace uso de Helper Class Enum <T> que se encuentra aquí y que actualicé para usar el yield return
para 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);
}
}
Finalmente, aquí hay un ejemplo de cómo usarlo:
private List<CountType> GetCountTypes(CountType countTypes)
{
List<CountType> cts = new List<CountType>();
foreach (var ct in countTypes.GetFlags())
cts.Add(ct);
return cts;
}
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<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;
}
}
}
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
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...
}
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 ...
/// <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;
}
}
}
Uso:
FileAttributes att = FileAttributes.Normal | FileAttributes.Compressed;
foreach (FileAttributes fa in att.GetFlags())
{
...
}
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.
Saliendo del método de @ Greg, pero agregando una nueva característica de C # 7.3, la restricción Enum
:
public static IEnumerable<T> GetUniqueFlags<T>(this Enum flags)
where T : Enum // New constraint for C# 7.3
{
foreach (Enum value in Enum.GetValues(input.GetType()))
if (input.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
.
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<Enum> 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 | ...
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<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;
}
}
}
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).
static IEnumerable<Enum> GetFlags(Enum input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
if (input.HasFlag(value))
yield return value;
}