比较C#中的枚举标志

我需要检测是否在枚举值中设置了一个标志,哪个类型用Flag属性标记。

通常它是这样的:

(value & flag) == flag 

但是因为我需要通用generics(有时在运行时我的事件只有一个“Enum”引用。我找不到一个简单的方法来使用&运算符。目前我这样做:

  public static bool IsSet(this T value, T flags) where T : Enum { Type numberType = Enum.GetUnderlyingType(typeof(T)); if (numberType.Equals(typeof(int))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(sbyte))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(byte))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(short))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(ushort))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(uint))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(long))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(ulong))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(char))) { return BoxUnbox(value, flags, (a, b) => (a & b) == b); } else { throw new ArgumentException("Unknown enum underlying type " + numberType.Name + "."); } } private static bool BoxUnbox(object value, object flags, Func op) { return op((T)value, (T)flags); } 

但是我不喜欢永无止境的if – else块,所以有没有办法转换这些值,我可以使用&运算符或任何其他解决方案来检查这个?

就个人而言,我觉得这很好,因为你把它包装成一个单一的目的函数。 如果你将代码分散在整个程序中,我认为你会遇到一些问题,但是你所创建的内容可以提高所使用的清晰度,而且function本身就足够清晰。

当然只是我的意见。

但是,您可以使用is关键字,这可能会有所帮助

 public static bool IsSet(this T value, T flags) where T : Enum { if (value is int) { return ((int)(object)a & (int)(object)b) == (int)(object)b); } //etc... 

对我来说,它看起来过于复杂。 这个怎么样(请记住,枚举总是映射到整数值类型):

 public static bool IsSet(T value, T flags) where T : struct { // You can add enum type checking to be perfectly sure that T is enum, this have some cost however // if (!typeof(T).IsEnum) // throw new ArgumentException(); long longFlags = Convert.ToInt64(flags); return (Convert.ToInt64(value) & longFlags) == longFlags; } 

我为枚举编写了一组扩展方法,以备你需要时:

 public static class EnumExtensions { private static void CheckEnumWithFlags() { if (!typeof(T).IsEnum) throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName)); if (!Attribute.IsDefined(typeof(T), typeof(FlagsAttribute))) throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName)); } public static bool IsFlagSet(this T value, T flag) where T : struct { CheckEnumWithFlags(); long lValue = Convert.ToInt64(value); long lFlag = Convert.ToInt64(flag); return (lValue & lFlag) != 0; } public static IEnumerable GetFlags(this T value) where T : struct { CheckEnumWithFlags(); foreach (T flag in Enum.GetValues(typeof(T)).Cast()) { if (value.IsFlagSet(flag)) yield return flag; } } public static T SetFlags(this T value, T flags, bool on) where T : struct { CheckEnumWithFlags(); long lValue = Convert.ToInt64(value); long lFlag = Convert.ToInt64(flags); if (on) { lValue |= lFlag; } else { lValue &= (~lFlag); } return (T)Enum.ToObject(typeof(T), lValue); } public static T SetFlags(this T value, T flags) where T : struct { return value.SetFlags(flags, true); } public static T ClearFlags(this T value, T flags) where T : struct { return value.SetFlags(flags, false); } public static T CombineFlags(this IEnumerable flags) where T : struct { CheckEnumWithFlags(); long lValue = 0; foreach (T flag in flags) { long lFlag = Convert.ToInt64(flag); lValue |= lFlag; } return (T)Enum.ToObject(typeof(T), lValue); } } 

主要的缺点是你不能指定where T : Enum :它是明确禁止的(“约束不能是特殊类’System.Enum’”),所以扩展方法将出现在intellisense中所有结构…我添加CheckEnumWithFlags方法检查该类型实际上是枚举,并具有Flags属性。


更新:Jon Skeet最近创建了一个名为UnconstrainedMelody的有趣库,它完成了同样的事情,并且解决了上面提到的generics类型约束限制

这应该为具有任何基础类型的enum类型执行任务:

 public static bool IsSet(this T value, T flags) where T : struct { return (Convert.ToInt64(value) & Convert.ToInt64(flags)) == Convert.ToInt64(flags); } 

Convert.ToInt64是因为64位整数是可能的“最宽”整数类型,所有枚举值都可以转换为(甚至是ulong )。 请注意, char不是有效的基础类型。 它似乎在C#中无效,但它通常在CIL / CLR中有效。

此外,您不能为枚举强制执行generics类型约束(即where T : struct ); 您可以做的最好的事情是使用where T : struct来强制T为值类型,然后可选地执行动态检查以确保T是枚举类型。

为了完整起见,这是我非常简短的测试工具:

 static class Program { static void Main(string[] args) { Debug.Assert(Foo.abc.IsSet(Foo.abc)); Debug.Assert(Bar.def.IsSet(Bar.def)); Debug.Assert(Baz.ghi.IsSet(Baz.ghi)); } enum Foo : int { abc = 1, def = 10, ghi = 100 } enum Bar : sbyte { abc = 1, def = 10, ghi = 100 } enum Baz : ulong { abc = 1, def = 10, ghi = 100 } } 

只需使用Enum.HasFlag()方法!

我用它来比较标志

 public static bool IsSet(this T input, T match) { return (Convert.ToUInt32(input) & Convert.ToUInt32(match)) != 0; } 

在这里,您可以进行不同的转换。 从int到short到long。

或者… public static bool IsSet(此枚举值,枚举比较){int baseValue = value.ToInt32(); int compareValue = compare.ToInt32(); if(baseValue == 0)返回false; return((baseValue&compareValue)== compareValue); }