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

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

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

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

不,你不能用C#generics做到这一点。 但是,您可以这样做:

 public static bool IsEnumFlagPresent(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 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)); } } 

您是否希望用包含一行代码的函数替换一行代码? 我要说只使用一行代码……

为了它的价值,我最近读到这个function将成为.NET 4.0的一部分。 具体来说,它是在Enum.HasFlag()函数中实现的。

我之前用过这个:

 public static bool In(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)) { // ... } 

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

 public static class EnumerationExtensions { public static bool Has(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),但它可以让你开始……

您可以在没有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); } 

我在这种情况下转换为Int64,它应该处理除ulong之外的所有情况,这就是额外检查的原因……

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

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

您现在无法在不使用C ++ / CLI的情况下将通用类型结构快速转换为某种备用按位forms(即粗略地说是reinterpret_cast)

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

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

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

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

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

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

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

  public static bool HasFlag(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; } 

下面是对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 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(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