16

我想要一个可以与任何标志样式枚举一起使用的通用函数,以查看标志是否存在。

这不会编译,但如果有人有建议,我将不胜感激。

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

11 回答 11

26

不,你不能用 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));
    }
}
于 2009-06-12T16:26:02.703 回答
23

您想用包含一行代码的函数替换一行代码?我会说只使用一行代码......

于 2009-06-12T16:26:08.057 回答
15

对于它的价值,我最近读到此功能将成为 .NET 4.0 的一部分。具体来说,它是在Enum.HasFlag()函数中实现的。

于 2009-10-29T00:51:16.360 回答
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))
{
   // ...
}
于 2009-06-12T16:47:23.200 回答
2

您可以在没有泛型的情况下执行此操作:

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 之外的所有情况,这就是为什么额外检查...

于 2009-06-12T16:38:18.667 回答
2

为什么不为此编写扩展方法?我在另一篇文章中这样做了

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),但它可以让你开始......

于 2009-06-12T16:47:22.637 回答
1

值得指出的是,只要您知道您正在使用特定的枚举,只需为所有整数类型提供一些静态重载即可。如果消费代码同样在运行,它们将无法工作where t : struct

如果需要处理任意(struct)T

如果不使用 C++/CLI,您目前无法将泛型类型的结构快速转换为另一种按位形式(即粗略地说是 reinterpret_cast)

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

您不能限制为枚举。但是,如果这些方法与非枚举类型一起使用,它们的数学有效性不会改变,因此如果您可以确定它们可转换为相关大小的结构,则可以允许它们。

于 2009-06-12T16:40:29.223 回答
1

问题很久了,但无论如何这里有一个供参考:

    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;
    }
于 2009-11-26T18:16:40.140 回答
1

今天可以将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));
    }
}
于 2019-01-30T19:52:01.470 回答
0

好吧,我不相信有办法做到这一点,因为没有适用于位运算符的约束。

但是...您可以将枚举转换为 int 并执行此操作。

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

这可行,但可能会让某人感到困惑。

于 2009-06-12T16:39:59.800 回答
0

下面是对 4 种不同方法进行基准测试的代码。结果显示在注释“BENCHMARK: .. nSec”的代码中。

"((enum & flag) != 0)' 比 HasFlag() 函数快 10 倍。如果您处于紧密循环中,那么我认为最好接受它。

    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;
    }
于 2018-06-19T15:05:52.393 回答