是否可以创建一个通用的按位枚举’IsOptionSet()’方法?

下面的代码可以很容易地传入一个HtmlParserOptions集合,然后检查一个选项以查看它是否被选中。

[Flags] public enum HtmlParserOptions { NotifyOpeningTags = 1, NotifyClosingTags = 2, NotifyText = 4, NotifyEmptyText = 8 } private bool IsOptionSet(HtmlParserOptions options, HtmlParserOptions singleOption) { return (options & singleOption) == singleOption; } 

我的问题是,是否可以创建一个通用版本(我猜通过在方法属性上实现一个接口),它将与任何带有Flags属性的枚举一起使用?

编辑:

最简单,最好的选择是升级到VS2010 Beta2并使用.NET 4的Enum.HasFlag方法。 框架团队为Enum添加了许多不错的附加function,以使它们更好用。


原始(对于当前的.NET):

你可以通过传递枚举而不是generics来做到这一点:

 static class EnumExtensions { private static bool IsSignedTypeCode(TypeCode code) { switch (code) { case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: return false; default: return true; } } public static bool IsOptionSet(this Enum value, Enum option) { if (IsSignedTypeCode(value.GetTypeCode())) { long longVal = Convert.ToInt64(value); long longOpt = Convert.ToInt64(option); return (longVal & longOpt) == longOpt; } else { ulong longVal = Convert.ToUInt64(value); ulong longOpt = Convert.ToUInt64(option); return (longVal & longOpt) == longOpt; } } } 

这非常有效,如下所示:

 class Program { static void Main(string[] args) { HtmlParserOptions opt1 = HtmlParserOptions.NotifyText | HtmlParserOptions.NotifyEmptyText; Console.WriteLine("Text: {0}", opt1.IsOptionSet(HtmlParserOptions.NotifyText)); Console.WriteLine("OpeningTags: {0}", opt1.IsOptionSet(HtmlParserOptions.NotifyOpeningTags)); Console.ReadKey(); } } 

以上打印:

 Text: True OpeningTags: False 

但是,它的缺点是它不能保护您不会将两种不同类型的Enum类型传递到例程中。 你必须合理地使用它。

好吧,有点。

你不能添加一个约束来确保type参数是一个“flags”枚举,并且在普通的C#中你不能添加一个约束来确保它首先是一个枚举…但是有点jiggery-pokery你可以让后者工作。 它是IL中的有效约束,但不是C#中的约束。 然后,您需要做一些工作才能使“和”部分正常工作。

我有一个名为Unconstrained Melody的项目,它通过一些IL重写在枚举上有一些有用的扩展方法。 在这种情况下,您将使用:

 if (options.HasAny(optionToTest)) 

要么

 if (options.HasAll(optionToTest)) 

取决于你想如何处理optionToTest实际上是多个组合标志的情况。

或者,等待.NET 4.0 – BCL中的更改包括Enum.HasFlag ,我认为它会做你想要的。

 public static bool IsOptionSet(this T flags, T option) where T : struct { if(! flags is int) throw new ArgumentException("Flags must be int"); int opt = (int)(object)option; int fl = (int)(object)flags; return (fl & opt) == opt; } 

编辑:正如评论中指出的那样,如果枚举不是int(这是枚举的默认值),这将不起作用。 它可能应该被命名为其他东西以表示这一点,但对于大多数情况它可能“足够好”,除非你需要一组超过31个值的标志。