问题
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?
回答1:
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.
回答2:
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.
回答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;
}
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.
来源:https://stackoverflow.com/questions/1614412/is-it-possible-to-create-a-generic-bitwise-enumeration-isoptionset-method