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

时间:2009-10-23 15:58:42

标签: c# .net enums bit-manipulation

下面的代码可以很容易地传入一组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属性的任何枚举一起使用?< / p>

3 个答案:

答案 0 :(得分:7)

编辑:

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


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

你可以通过传递Enum而不是泛型来实现这一点:

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

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

答案 1 :(得分:3)

public static bool IsOptionSet<T>(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个值的标志。

答案 2 :(得分:3)

嗯,好吧。

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

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

if (options.HasAny(optionToTest))

if (options.HasAll(optionToTest))

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

或者,等待.NET 4.0 - changes in the BCL包括Enum.HasFlag,我认为它会做你想要的。