2009-06-12 17 views
12

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 marca.C#, Flags Enum, función genérica para buscar una marca

Esto no se compila, pero si alguien tiene alguna sugerencia, se lo agradecería.

public static Boolean IsEnumFlagPresent<T>(T value,T lookingForFlag) 
     where T:enum 
{ 
    Boolean result = ((value & lookingForFlag) == lookingForFlag); 
    return result ;    
} 
+3

Sólo una nota: En C# 4 la estructura de enumeración proporciona un método HasFlag estándar(). –

Respuesta

18

No, no puede hacer esto con C# generics. Sin embargo, usted podría 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 sólo funcionará para las enumeraciones que tienen un tipo subyacente de int, y es algo ineficaz, ya que el valor de las cajas ... pero debería funcionar.

Es posible que desee agregar un control 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 demuestra que funcione:

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)); 
    } 
} 
+1

Esto le permitiría pasar algo que no es un Enum y no ser muy seguro. Si va a boxear, ¿por qué no simplemente pasa el parámetro como System.Enum en lugar de usar genéricos? –

+1

Cierto, eso también lo haría ... Permitiría pasar "nulo" para ambos valores, por cierto, por lo que todavía necesitaría una verificación en tiempo de ejecución ... –

+0

Otro beneficio de hacerlo genérico es que esto verifica que ambos valores sean del * mismo * tipo. No puede pasar en ErrorCode.Foo, HttpStatusCode.Bar. –

21

Usted está buscando para reemplazar a uno línea de código con una función que envuelve una línea de código? Yo diría que sólo tiene que utilizar la una línea de código ...

+3

Santa vaca, tengo más votos ascendentes que Skeet. Esto no puede durar ... :-) –

+6

Es una buena respuesta, y ni siquiera * pensé * en responder con eso. Fui directamente al lado de la implementación de las cosas. Nota para uno mismo: debe dar un paso atrás ... –

+1

Vale la pena señalar que la necesidad de hacer la verificación! = 0 == 0 dependiendo del deseo es algo dolorosa para las personas que no están acostumbradas al tipo de estilo c/C++ ser un poco molesto El punto aún se mantiene, así que +1 – ShuggyCoUk

1

Puede hacer esto sin los 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); 
} 

estoy convirtiendo a Int64 en este caso, que debe manejar todos los casos, excepto ulong , por lo que el cheque adicional ...

+0

vale la pena señalar que esta cajas. Sin embargo, podría no ser un problema – ShuggyCoUk

+0

Sí, cierto. Lo mencioné en mi comentario a la publicación de Jon Skeet, pero no lo puse en mi respuesta. –

0

Bueno, no creo que haya una manera de hacerlo, ya que no hay restricciones que se apliquen a los operadores bit a bit.

Sin embargo ... solo puede enviar su 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.

+0

tenga en cuenta que esto no funcionará para las enumeraciones que no son ints ... efectivo para el caso común, aunque – ShuggyCoUk

+0

Es cierto que tendría que proporcionar sobrecargas para otros tipos de valores. También tenga en cuenta que no puede usar ValueType ya que no funciona con operadores bit a bit. – Will

1

Vale la pena señalar que el simple hecho de 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 consumir está funcionando del mismo modo en where t : struct

Si tiene que lidiar con (struct) arbitraria T

Actualmente no puede hacer una conversión rápida de una estructura genéricamente escrito en alguna forma bit a bit alternativo (es decir, en términos generales un reinterpret_cast) sin necesidad de 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 hará que pueda 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 puede restringir las enumeraciones. Pero la validez matemática de estos métodos no cambia si se usan con tipos que no son enum, por lo que podría permitirlos si puede determinar que son convertibles a una estructura del tamaño relevante.

2

¿Por qué no escribir un método de extensión para esto?I did this in another post

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

Se podría utilizar un poco de refinamiento (ya que se supone que todo puede ser echado como un int), pero podría empezar ...

6

he utilizado esto 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 puede inferir parámetros genéricos.

AttributeTargets a = AttributeTargets.Class; 
if (a.In(AttributeTargets.Class | AttributeTargets.Module)) 
{ 
    // ... 
} 
+0

Eso es bastante bueno – Hugoware

9

Por lo que vale, recientemente leí que esta característica será parte de .NET 4.0. Específicamente, se implementa en la función Enum.HasFlag().

+0

De hecho lo es, me acabo de enterar. – Trax72

0

Pregunta largo sobre, 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; 
    } 
Cuestiones relacionadas