2009-06-12 3 views
12

Мне нужна одна функция общего назначения, которая может быть использована с любым перечислением стиля Flags, чтобы увидеть, существует ли флаг.C#, Flags Enum, Generic function для поиска флага

Это не скомпилировано, но если у кого-то есть предложение, я был бы признателен.

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

Только примечание: В C# 4 структура Enum обеспечивает стандартный метод HasFlag(). –

ответ

18

Нет, вы не можете сделать это с помощью C# generics. Тем не менее, вы могли сделать:

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

Это будет работать только для перечислений, которые имеют базовый тип int, и это несколько неэффективно, потому что это коробки значение ... но он должен работать.

Вы можете добавить проверку типа исполнения, что T является фактически типом перечисления (например typeof(T).BaseType == typeof(Enum))

Вот полная программа демонстрирует это работает:

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

Это позволит вам передать что-то, что не является Enum, хотя и не очень безопасно. Если вы собираетесь вставить, почему бы просто не передать параметр как System.Enum вместо использования дженериков? –

+1

Правда, это тоже сделало бы ... Это позволит вам пройти «нуль» для обоих значений, по общему признанию, так что вам все равно нужна проверка времени выполнения ... –

+0

Другим преимуществом создания этого является то, что это проверяет, что оба значения являются * одинаковыми *. Вы не можете пройти в ErrorCode.Foo, HttpStatusCode.Bar. –

21

Вы хотите заменить один строка кода с функцией, которая обертывает одну строку кода? Я бы сказал, просто использовать одну строку кода ...

+3

Священная корова, у меня больше болей, чем у Скита. Это не может продолжаться ... :-) –

+6

Это хороший ответ, и я даже не подумал об этом. Я пошел прямо в сторону реализации вещей. Примечание для себя: должно сделать шаг назад ... –

+1

Стоит отметить, что необходимость делать проверку! = 0 == 0 в зависимости от желания несколько болезненна для людей, не привыкших к стилю c/C++. быть немного раздражающим. Точка по-прежнему сохраняется, хотя так +1 – ShuggyCoUk

1

Вы можете сделать это без дженериков:

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

Я преобразовывая в Int64 в этом случае, который должен обрабатывать каждый случай, за исключением ULONG , поэтому дополнительная проверка ...

+0

Стоит отметить, что это коробки. может быть, не проблема, но – ShuggyCoUk

+0

Да, правда. Я упомянул об этом в своем комментарии к сообщению Джона Скита, но пренебрег тем, что поставил его в свой ответ. –

0

Ну, я не верю, что есть способ сделать это, так как нет ограничений, которые применяются к побитовым операторам.

Однако ... вы можете просто перевести свой enum в int и сделать это.

public static Boolean IsEnumFlagPresent(int value,int lookingForFlag) 
{ 
    return ((value & lookingForFlag) == lookingForFlag); 
} 

Это работает, но может смущать кого-то.

+0

Обратите внимание, что это не будет работать для перечислений, которые не являются int ... эффективными для общего случая, но – ShuggyCoUk

+0

Правда, вам нужно будет предоставить перегрузки для других типов значений. Также обратите внимание, что вы не можете просто использовать ValueType, поскольку он не работает с побитовыми операторами. – Will

1

Стоит отметить, что просто предоставление некоторых статических перегрузок для всех интегральных типов будет работать, пока вы знаете, что работаете с определенным перечислением. Они не будут работать, если потребляя код также работает на where t : struct

Если вам придется иметь дело с произвольным (STRUCT) T

Вы не можете себе сделать быструю конверсию обобщенно типизированной структуры в некоторой альтернативной форме побитового (т.е. грубо говоря reinterpret_cast) без использования 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)); 
    } 
} 

Это будет препятствовать вам написать:

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...   
} 

Вы не можете ограничивать перечисления. Но математическая обоснованность этих методов не изменяется, если они используются с типами без перечисления, поэтому вы можете разрешить им, если сможете определить, что они конвертируются в структуру соответствующего размера.

2

Почему бы не написать метод расширения для этого?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); 

Он мог бы использовать немного утонченности (так как он предполагает, все может быть брошено как целое число), но это может заставить вас начать ...

6

Я использовал это раньше:

public static bool In<T>(this T me, T values) 
    where T : struct, IConvertible 
{ 
    return (me.ToInt64(null) & values.ToInt64(null)) > 0; 
} 

Что мне нравится в этом, вы можете использовать этот чистый синтаксис для вызова его, поскольку в 3.5 компилятор может вывести общие параметры.

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

Это довольно хорошо – Hugoware

9

Для чего стоит, недавно я прочитал, что эта функция будет частью .NET 4.0. В частности, он реализован в функции Enum.HasFlag().

+0

Действительно, только что выяснилось. – Trax72

0

Вопрос давно закончилась, но вот один для справки в любом случае:

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