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

发布于 2024-08-08 22:34:33 字数 450 浏览 4 评论 0原文

下面的代码可以轻松传递一组 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 属性的任何枚举一起使用?

The below code makes it easy to pass in a set HtmlParserOptions and then check against a single option to see if it's been selected.

[Flags]
public enum HtmlParserOptions
{
    NotifyOpeningTags = 1,
    NotifyClosingTags = 2,
    NotifyText = 4,
    NotifyEmptyText = 8
}

private bool IsOptionSet(HtmlParserOptions options, HtmlParserOptions singleOption)
{
    return (options & singleOption) == singleOption;
}

My question is, is it possible to create a Generic version of this (I'm guessing through implementing an interface on the method properties) that will work with any enumeration with the Flags attribute?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

愿得七秒忆 2024-08-15 22:34:33

编辑:

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


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

您可以通过传递枚举而不是泛型来做到这一点:

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

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

Edit:

The easiest, nicest option is to upgrade to VS2010 Beta2 and use .NET 4's Enum.HasFlag method. The framework team has added a lot of nice additions to Enum to make them nicer to use.


Original (for current .NET):

You can do this by passing Enum, instead of 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;
        }
    }
}

This works perfectly, like so:

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();
    } 
}

THe above prints:

Text: True
OpeningTags: False

The downside to this, though, is it doesn't protect you from passing two different types of Enum types into the routine. You have to use it reasonably.

明天过后 2024-08-15 22:34:33

嗯,有点像。

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

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

if (options.HasAny(optionToTest))

或 ,

if (options.HasAll(optionToTest))

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

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

Well, sort of.

You can't add a constraint to make sure that the type argument is a "flags" enum, and in plain C# you can't add a constraint to make sure that it's an enum in the first place... but with a bit of jiggery-pokery you can get the latter to work. It's a valid constraint in IL, but not in C#. You'd then need to do a bit of work to get the "and" part working generically.

I have a project called Unconstrained Melody which has some useful extension methods on enums, via some IL-rewriting. In this case you'd use:

if (options.HasAny(optionToTest))

or

if (options.HasAll(optionToTest))

depending on how you wanted to handle the case where optionToTest is actually multiple combined flags.

Alternatively, wait for .NET 4.0 - the changes in the BCL include Enum.HasFlag, which I think will do what you want.

醉南桥 2024-08-15 22:34:33
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 个值的标志。

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;
}

EDIT: As has been pointed out in the comments, this won't work if the enum is anything other than an int (which is the default for enums). It should be probably be named something else to indicate this, but it's probably 'good enough' for the majority of cases unless you need a set of flags with more than 31 values.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文