switch - C#, Flags Enum, función genérica para buscar una bandera
enum in model c# (9)
Me gustaría una función de propósito general que se pueda usar con cualquier enum de estilo de Flags para ver si existe una bandera.
Esto no compila, pero si alguien tiene una sugerencia, lo agradecería.
public static Boolean IsEnumFlagPresent<T>(T value,T lookingForFlag)
where T:enum
{
Boolean result = ((value & lookingForFlag) == lookingForFlag);
return result ;
}
¿Está buscando reemplazar una línea de código con una función que envuelve una línea de código? Yo diría que solo use una línea de código ...
Bueno, no creo que haya una manera de hacerlo, ya que no existen restricciones que se apliquen a los operadores bit a bit.
Sin embargo ... puedes enviar tu enumeración a int y hacerlo.
public static Boolean IsEnumFlagPresent(int value,int lookingForFlag)
{
return ((value & lookingForFlag) == lookingForFlag);
}
Esto funciona, pero puede ser confuso para alguien.
Lo he usado antes:
public static bool In<T>(this T me, T values)
where T : struct, IConvertible
{
return (me.ToInt64(null) & values.ToInt64(null)) > 0;
}
Lo que me gusta de él es que puede usar esta sintaxis limpia para llamarlo, ya que en 3.5 el compilador podrá inferir parámetros genéricos.
AttributeTargets a = AttributeTargets.Class;
if (a.In(AttributeTargets.Class | AttributeTargets.Module))
{
// ...
}
No, no puedes hacer esto con los genéricos C #. Sin embargo, podrías hacer:
public static bool IsEnumFlagPresent<T>(T value, T lookingForFlag)
where T : struct
{
int intValue = (int) (object) value;
int intLookingForFlag = (int) (object) lookingForFlag;
return ((intValue & intLookingForFlag) == intLookingForFlag);
}
Esto solo funcionará para enumeraciones que tienen un tipo subyacente de int
, y es algo ineficiente porque encapsula el valor ... pero debería funcionar.
Es posible que desee agregar una verificación de tipo de ejecución que T es en realidad un tipo de enumeración (por ejemplo, typeof(T).BaseType == typeof(Enum)
)
Aquí hay un programa completo que lo demuestra funcionando:
using System;
[Flags]
enum Foo
{
A = 1,
B = 2,
C = 4,
D = 8
}
class Test
{
public static Boolean IsEnumFlagPresent<T>(T value, T lookingForFlag)
where T : struct
{
int intValue = (int) (object) value;
int intLookingForFlag = (int) (object) lookingForFlag;
return ((intValue & intLookingForFlag) == intLookingForFlag);
}
static void Main()
{
Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.A));
Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.B));
Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.C));
Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.D));
}
}
Puedes hacer esto sin genéricos:
static bool ContainsFlags(Enum value, Enum flag)
{
if (Enum.GetUnderlyingType(value.GetType()) == typeof(ulong))
return (Convert.ToUInt64(value) & Convert.ToUInt64(flag)) == Convert.ToUInt64(flag);
else
return (Convert.ToInt64(value) & Convert.ToInt64(flag)) == Convert.ToInt64(flag);
}
Me estoy convirtiendo a Int64 en este caso, que debe manejar todos los casos excepto ulong, por lo que el cheque adicional ...
Vale la pena señalar que simplemente proporcionar algunas sobrecargas estáticas para todos los tipos integrales funcionará siempre que sepa que está trabajando con una enumeración específica. No funcionarán si el código de consumo también está funcionando en where t : struct
Si necesita tratar con arbitrario (struct) T
Actualmente, no puede realizar una conversión rápida de una estructura genéricamente tipada en una forma bitérica alternativa (es decir, aproximadamente una reinterpretación_cast) sin utilizar C ++ / CLI
generic <typename T>
where T : value class
public ref struct Reinterpret
{
private:
const static int size = sizeof(T);
public:
static int AsInt(T t)
{
return *((Int32*) (void*) (&t));
}
}
Esto luego te permitirá escribir:
static void IsSet<T>(T value, T flags) where T : struct
{
if (!typeof(T).IsEnum)
throw new InvalidOperationException(typeof(T).Name +" is not an enum!");
Type t = Enum.GetUnderlyingType(typeof(T));
if (t == typeof(int))
{
return (Reinterpret.AsInt(value) & Reinterpret.AsInt(flags)) != 0
}
else if (t == typeof(byte))
{
return (Reinterpret.AsByte(value) & Reinterpret.AsByte(flags)) != 0
}
// you get the idea...
}
No puedes restringir a enumeraciones. Pero la validez matemática de estos métodos no cambia si se usan con tipos no enum, por lo que podría permitirlos si puede determinar que son convertibles en una estructura del tamaño relevante.
Por lo que vale, recientemente leí que esta característica formará parte de .NET 4.0. Específicamente, se implementa en la función Enum.HasFlag()
.
Cuestión larga, pero aquí hay uno para referencia de todos modos:
public static bool HasFlag<TEnum>(this TEnum enumeratedType, TEnum value)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
if (!(enumeratedType is Enum))
{
throw new InvalidOperationException("Struct is not an Enum.");
}
if (typeof(TEnum).GetCustomAttributes(
typeof(FlagsAttribute), false).Length == 0)
{
throw new InvalidOperationException("Enum must use [Flags].");
}
long enumValue = enumeratedType.ToInt64(CultureInfo.InvariantCulture);
long flagValue = value.ToInt64(CultureInfo.InvariantCulture);
if ((enumValue & flagValue) == flagValue)
{
return true;
}
return false;
}
¿Por qué no escribir un método de extensión para esto? Lo hice en otra publicación
public static class EnumerationExtensions {
public static bool Has<T>(this System.Enum type, T value) {
try {
return (((int)(object)type & (int)(object)value) == (int)(object)value);
}
catch {
return false;
}
}
//... etc...
}
//Then use it like this
bool hasValue = permissions.Has(PermissionTypes.Delete);
Podría usar un pequeño refinamiento (ya que supone que todo se puede convertir como un int), pero podría comenzar ...