C #, Flags Enum, универсальная функция для поиска флага - PullRequest
13 голосов
/ 12 июня 2009

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

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

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

Ответы [ 11 ]

21 голосов
/ 12 июня 2009

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

20 голосов
/ 12 июня 2009

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

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));
    }
}
11 голосов
/ 29 октября 2009

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

6 голосов
/ 12 июня 2009

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

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))
{
   // ...
}
2 голосов
/ 12 июня 2009

Почему бы не написать метод расширения для этого? Я сделал это в другом посте

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

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

2 голосов
/ 12 июня 2009

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

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, поэтому дополнительная проверка ...

1 голос
/ 12 июня 2009

Стоит отметить, что простое предоставление статических перегрузок для всех целочисленных типов будет работать до тех пор, пока вы знаете, что работаете с конкретным перечислением. Они не будут работать, если код потребления также работает на where t : 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...        
}

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

0 голосов
/ 30 января 2019

Сегодня вы можете установить версию языка c # на> = 7.3 и используйте следующий код в качестве ссылки:

public static class EnumExt
{
    public static IEnumerable<TEnum> Explode<TEnum>(this TEnum enumValue) where TEnum : Enum
    {
        var res = Enum.GetValues(enumValue.GetType())
            .Cast<TEnum>()
            .Where(x => enumValue.HasFlag(x));
        return res;
    }

    public static string ExplodeToString<TEnum>(this TEnum enumValue, string delimeter = ",") where TEnum : Enum
    {
        return string.Join(delimeter, Explode(enumValue));
    }
}
0 голосов
/ 19 июня 2018

ниже - код, который тестирует 4 различных метода. результаты показаны в коде в комментарии "BENCHMARK: .. nSec".

"((enum & flag)! = 0) 'в 10 раз быстрее функции HasFlag (). Если вы находитесь в тесном цикле, то я думаю, что лучше всего принять это.

    public static int jumpCtr=0;
    public static int ctr=0;
    public static TestFlags gTestFlags = TestFlags.C;
    [Flags] public enum TestFlags { A=1<<1, B=1<<2, C=1<<3 }
    public static void Jump()  { jumpCtr++; gTestFlags = (gTestFlags == TestFlags.B) ? TestFlags.C : TestFlags.B;  }

    // IsEnumFlagPresent() /724645/c-flags-enum-universalnaya-funktsiya-dlya-poiska-flaga
    [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasFlag_Faster<T>(T value, T lookingForFlag) 
        where T : struct
    {
        int intValue                = (int) (object) value;
        int intLookingForFlag       = (int) (object) lookingForFlag;
        return ((intValue & intLookingForFlag) != 0);
    }

    // IsEnumFlagPresent() /724645/c-flags-enum-universalnaya-funktsiya-dlya-poiska-flaga
    [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasFlag_Faster_Integer(int intValue, int intLookingForFlag) 
    {
        return ((intValue & intLookingForFlag) != 0);
    }

    public static void Benchmark_HasFlag( )  
    {
        if ( ! hwDvr._weAreOnGswCpu) { return; }

        DateTime timer = DateTime.Now; 
        string a, b, c, d, e;
        double base_nSecPerLoop, b_nSecPerLoop, c_nSecPerLoop, d_nSecPerLoop, e_nSecPerLoop;
        int numOfLoops = (int) 1.0e6;

        //  ------------------------------------------------------
        for (int i=0; i<numOfLoops;i++) {
            Jump();
        }
        a = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out base_nSecPerLoop);

        //  ------------------------------------------------------
        //  BENCHMARK: 50 nSec

        for (int i=0; i<numOfLoops;i++) {
            if (gTestFlags.HasFlag((TestFlags) TestFlags.C)) {   
                ctr++;
            }
            Jump();
        }
        b = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out b_nSecPerLoop );

        double b_diff = b_nSecPerLoop - base_nSecPerLoop;

        //  ------------------------------------------------------
        //  BENCHMARK: 3 nSec

        for (int i=0; i<numOfLoops;i++) {
            if ((gTestFlags & TestFlags.C) != 0) {   
                ctr++;
            }
            Jump();
        }
        c = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out c_nSecPerLoop );

        double c_diff = c_nSecPerLoop - base_nSecPerLoop;

        //  ------------------------------------------------------
        //  BENCHMARK: 64 nSec

        for (int i=0; i<numOfLoops;i++) {
            if (HasFlag_Faster<TestFlags>(value:gTestFlags, lookingForFlag: TestFlags.C)) {   
                ctr++;
            }
            Jump();
        }
        d = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out d_nSecPerLoop );

        double d_diff = d_nSecPerLoop - base_nSecPerLoop;

        //  ------------------------------------------------------
        //  BENCHMARK: 14 nSec

        for (int i=0; i<numOfLoops;i++) {
            if (HasFlag_Faster_Integer((int)gTestFlags, (int)TestFlags.C)) {   
                ctr++;
            }
            Jump();
        }
        e = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out e_nSecPerLoop );

        double e_diff = e_nSecPerLoop - base_nSecPerLoop;

        int brkPt=0;
    }
0 голосов
/ 26 ноября 2009

Вопрос давно закончился, но вот один для справки:

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