C#, Flags Enum, 查找标志的通用函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/987607/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:59:14  来源:igfitidea点击:

C#, Flags Enum, Generic function to look for a flag

c#enumsenum-flags

提问by jeff

I'd like one general purpose function that could be used with any Flags style enum to see if a flag exists.

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

This doesn't compile, but if anyone has a suggestion, I'd appreciate it.

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

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

采纳答案by Jon Skeet

No, you can't do this with C# generics. However, you coulddo:

不,你不能用 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);
}

This will only work for enums which have an underlying type of int, and it's somewhat inefficient because it boxes the value... but it should work.

这仅适用于具有基础类型的枚举int,并且效率有点低,因为它将值装箱......但它应该可以工作。

You may want to add an execution type check that T is actually an enum type (e.g. typeof(T).BaseType == typeof(Enum))

您可能想要添加一个执行类型检查 T 实际上是一个枚举类型(例如typeof(T).BaseType == typeof(Enum)

Here's a complete program demonstrating it working:

这是一个完整的程序,展示了它的工作原理:

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

回答by Paul Sonier

You're looking to replace one line of code with a function that wraps one line of code? I'd say to just use the one line of code...

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

回答by Reed Copsey

You can do this without generics:

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

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

I'm converting to Int64 in this case, which should handle every case except ulong, which is why the extra check...

在这种情况下,我正在转换为 Int64,它应该处理除 ulong 之外的所有情况,这就是为什么要进行额外检查...

回答by Reed Copsey

Well, I don't believe there is a way to do this, as there are no constraints that apply to bitwise operators.

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

However... you can just cast your enum to int and do it.

但是......你可以将你的枚举转换为 int 并执行它。

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

This works, but may be confusing to someone.

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

回答by ShuggyCoUk

Worth pointing out that simply providing some static overloads for all the integral types will work so long as you know you are working with a specific enum. They won't work if the consuming code is likewise operating on where t : struct

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

If you need to deal with arbitrary (struct) T

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

You cannot currently do a fast conversion of a generically typed struct into some alternate bitwise form (i.e. roughly speaking a reinterpret_cast) without using C++/CLI

您目前无法在不使用 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));
    }
}

This will then let you write:

这将让你写:

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

You cannot constrain to enums. But the mathematical validity of these methods do not change if they are used with non enum types so you could allow them if you can determine that they are convertible to a struct of the relevant size.

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

回答by Hugoware

Why not write an extension method for this? 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);

It could use a little refinement (since it assumes everything can be cast as an int), but it could get you started...

它可以使用一些改进(因为它假设一切都可以转换为 int),但它可以让你开始......

回答by Kleinux

I have used this before:

我以前用过这个:

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

What I like about it is you can use this clean syntax to call it since in 3.5 the compiler will can infer generic parameters.

我喜欢它的是你可以使用这种干净的语法来调用它,因为在 3.5 中编译器可以推断出通用参数。

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

回答by Steve Guidi

For what its worth, I recently read that this feature will be part of .NET 4.0. Specifically, it is implemented in the Enum.HasFlag()function.

就其价值而言,我最近读到此功能将成为 .NET 4.0 的一部分。具体是在Enum.HasFlag()函数中实现的。

回答by x0n

Question long over, but here's one for reference anyway:

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

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

回答by gsw

below is code that benchmarks 4 different methods. results are shown in code in comment "BENCHMARK: .. nSec".

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

"((enum & flag) != 0)' is 10x faster than HasFlag() function. if you are in a tight loop, then i think it is best to accept it.

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