Подтвердить что ты не робот

С#, Flags Enum, универсальная функция для поиска флага

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

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

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

Ответ 1

Нет, вы не можете сделать это с помощью С# 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));
    }
}

Ответ 2

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

Ответ 3

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

Ответ 4

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

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))
{
   // ...
}

Ответ 5

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

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), но это может заставить вас начать...

Ответ 6

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

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

Ответ 7

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

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

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

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

Ответ 8

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

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

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

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

Ответ 9

Вопрос длинный, но здесь один для справки:

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

Ответ 10

ниже приведен код, который тестирует 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() https://stackoverflow.com/questions/987607/c-flags-enum-generic-function-to-look-for-a-flag
    [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() https://stackoverflow.com/questions/987607/c-flags-enum-generic-function-to-look-for-a-flag
    [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;
    }

Ответ 11

Сегодня вы можете установить версию языка С# на> = 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));
    }
}