查看枚举是否包含标志的通用扩展方法
考虑到这一点:
[Flags]
public enum MyEnum {
One = 1,
Two = 2,
Four = 4,
Eight = 8
}
public static class FlagsHelper
{
public static bool Contains(this MyEnum keys, MyEnum flag)
{
return (keys & flag) != 0;
}
}
是否可以编写适用于任何 enum
而不仅仅是 MyEnum
的 Contains 通用版本?
编辑:
在阅读您的答案后,这将是我的版本:
public static bool Contains(this Enum keys, Enum flag)
{
ulong keysVal = Convert.ToUInt64(keys);
ulong flagVal = Convert.ToUInt64(flag);
return (keysVal & flagVal) == flagVal;
}
刚刚意识到检查我检查的方式是一个坏主意(return (keys & flag) != 0;
),因为 flag
参数实际上可能是多个标志,并且常识性的做法是仅当 keys
包含所有标志时才返回 true。另外,我不会检查空值,甚至不会确保它们是相同的类型。我可能想要使用不同的类型。
Considering this:
[Flags]
public enum MyEnum {
One = 1,
Two = 2,
Four = 4,
Eight = 8
}
public static class FlagsHelper
{
public static bool Contains(this MyEnum keys, MyEnum flag)
{
return (keys & flag) != 0;
}
}
Is it possible to write a generic version of Contains that would work for any enum
and not just MyEnum
?
Edit:
This would be my version after reading your answers:
public static bool Contains(this Enum keys, Enum flag)
{
ulong keysVal = Convert.ToUInt64(keys);
ulong flagVal = Convert.ToUInt64(flag);
return (keysVal & flagVal) == flagVal;
}
Just realized is a bad idea to check the way I was checking (return (keys & flag) != 0;
), because the flag
parameter might be actually several flags and the common sense thing to do is return true only if keys
contains all of them. Also, I wouldn't check for null values or even make sure they are the same type. I might want to use different types.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
我的这个方法基于一堆 SO & Google 搜索,并使用 Reflector 来查看 MS 对 .NET 4 HasFlags 方法做了什么。
注意:
<块引用>
如果您将负数定义为标志枚举常量,请务必小心,因为许多标志位置可能会设置为 1,这可能会使您的代码变得混乱并导致编码错误。
I based this method off of a bunch of SO & Google searches, and a by using reflector to see what MS did for the .NET 4 HasFlags method.
Notes:
不确定您是否使用 .NET 4.0,但它带有静态方法
Enum.HasFlags()
。-- 代码已删除(已接受的解决方案已包含)--
Not sure if you're using .NET 4.0 or not, but it comes with the static method
Enum.HasFlags()
.-- Code Removed (the accepted solution has it already) --
这是我的方法,它是类型安全的,不会进行任何装箱或拆箱。如果类型不是枚举,它将引发异常。
如果您想将其转换为将类型化为 Enum 的公共静态方法,则可以使用一种技术,但它不能是扩展方法。
也不需要检查 null,因为结构约束也阻止了可为 null 的枚举。我认为没有太多工作可以改进此代码,除了可以用 F# 或 C++/CLI 编写它以便可以对其施加枚举约束之外。
这个想法是使用表达式树构建一个函数,如果它不是基于 ulong 的枚举,则将枚举转换为 long,或者 ulong 然后和它们,本质上产生:
返回值& flag == flag
因为我忘记了上面的表达式树是 .NET 4,所以只有以下方法应该在 .NET 3.5 中工作来创建相同的表达式树::
这个版本应该在 .NET 3.5 中编译,如果不编译我不明白为什么。
This is my approach this is Type safe and doesn't do any boxing or unboxing. It throws an exception if the type is not an enum.
There is a technique you can use if you want to turn it into a public static method that will be typed to Enum's, but it can't be an extension method then.
There is also no need to check for null, as the struct contraint blocks out nullable enum's as well. I don't think there is much to be done to improve this code, with the exception of maybe writing it in F# or C++/CLI so that you can put an enum constraint on it.
The idea is to build a function using expression trees that will convert the enum to either long if its anything but a ulong based enum, or ulong and then and them, essentially producing::
return value & flag == flag
As I forgot that the expression tree above is .NET 4 only the following method should work in .NET 3.5 to create the same expression tree::
this version should compile in .NET 3.5 and if it doesn't I can't understand why.
不幸的是,没有一个好的方法来制作这样的扩展方法。为了使其工作,您需要有一个对
enum
值进行操作的通用方法。不幸的是,没有办法将泛型参数限制为枚举我想出的最好的方法是以下
但是它在几个方面受到限制
enum
值基于int
。e
为null
则抛出异常Unfortunately no there is not a good way to make an extension method like this. In order for this to work you'd need to have a generic method which operated on
enum
values. Unfortunately there is no way to constrain generic arguments to be an enumThe best I've come up with is the following
However it's limited in several ways
enum
values areint
based.e
isnull
您基本上可以使用现有的扩展方法,方法是使用
Enum
类型而不是MyEnum
。那么问题是它不知道枚举是标志并且不允许使用&
运算符,因此您只需将枚举值转换为数字即可。以及一个良好衡量标准的单元测试:
You can basically use your existing extension method, byt use the
Enum
type instead ofMyEnum
. The problem then is that it doesn't know the enums are flags and won't allow the&
operator, so you just have to convert the enum values to numbers.And a unit test for good measure:
我在这里有另一种方法,我只是利用 Delegate.CreateDelegate 允许 Enum 及其底层类型的方法之间的转换这一事实快速地编写出来。下面的方法很像我之前的答案,但我觉得对于不了解表达式树语法的人来说可能更容易阅读。基本上我们知道 Enums 只有 8 种可能的底层类型,因此我们只需为它可以使用的每个调用创建一个静态方法。为了简洁起见,我使用了匿名方法,这些方法的名称恰好与可能的类型代码值相同。这种方法将在 .Net 3.5 中工作::
I have another approach here that I just cooked up quickly using the fact that Delegate.CreateDelegate allows conversion between methods for Enum's and their underlying types. The following approach is much like my previous answer but I feel might be easier to read for people who don't know expression tree syntax. Basically we know that Enums only have 8 possible underlying types, and so we just create a static method for each call it could use. Since I'm going for brevity I use anonymous methods which happened to be named the same thing as the possible typecode values.This approach will work in .Net 3.5::
为 .NET Framework 3.5 实现 HasFlag 函数的另一种方法。
此扩展方法支持枚举的所有可能类型(
byte
、sbyte
、short
、ushort
、>int
、uint
、long
和ulong
)。基本上,该方法检查给定的枚举是否有符号/无符号,并将标志转换为枚举支持的类型中具有最大大小的类型。然后,使用&
运算符执行简单的比较。正如其他帖子中所解释的,我们无法使用枚举定义泛型类型的约束,并且使用带有
struct
约束的泛型是没有意义的,因为开发人员可以插入其他枚举类型或结构然后。所以,我认为最好不要使用泛型方法。Another way of implementing HasFlag function for the .NET Framework 3.5.
This extension method supports all the possible types for an enumeration (
byte
,sbyte
,short
,ushort
,int
,uint
,long
andulong
). Basically, the method checks if the given enumeration is signed/unsigned and converts the flag to the type with the highest size of the supported types for an enumeration. Then, a simple comparison is performed using the&
operator.As explained in other posts, we cannot define a constraint of the generic type with an enumeration and it doesn't make sense to use generic with a
struct
constraint, because w developers could insert other enumerations types or structures then. So, I think it's better to not use generic method for that.这是一个非常高性能、非装箱、免分配、安全(即不受
unsafe
关键字影响)、无分支的解决方案。它的工作原理是将枚举值重新解释为
ulong
,无论其类型或大小如何。它通过将ulong
值 0 写入堆栈、将这些字节重新解释为一个或多个TEnum
的序列、写入TEnum
值来实现此目的到位置[0]
,然后读回ulong值。Here is a very performant, non-boxing, allocation-free, safe (i.e. free from the
unsafe
keyword), branchless solution.It works by reinterpreting the enum value as a
ulong
, regardless of its type or size. It achieves this by writingulong
value 0 onto the stack, reinterpreting those bytes as a sequence of one or moreTEnum
s, writing theTEnum
value into location[0]
, and then reading back the ulong value.这是一个应该有效的例子。
This is an example of something that should work.