下面的代码可以很容易地传入一组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添加了许多不错的附加功能,以便更好地使用它们。


原创(对于当前的.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

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

其他提示

嗯,好吧。

您无法添加约束以确保type参数是“flags”。枚举,在简单的C#中你不能添加一个约束来确保它首先是一个枚举...但是有一点jiggery-pokery你可以让后者工作。它是IL中的有效约束,但不是C#中的约束。然后你需要做一些工作来获得“和”。部分工作一般。

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

if (options.HasAny(optionToTest))

if (options.HasAll(optionToTest))

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

或者,等待.NET 4.0 - BCL中的变化包括 Enum.HasFlag ,我认为它会做你想做的事。

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个值的标志。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top