如何迭代具有标志的枚举的值?
如果我有一个包含标志枚举的变量,我可以以某种方式迭代该特定变量中的单位值吗?或者我是否必须使用 Enum.GetValues 来迭代整个枚举并检查设置了哪些?
If I have a variable holding a flags enum, can I somehow iterate over the single-bit values in that specific variable? Or do I have to use Enum.GetValues
to iterate over the entire enum and check which ones are set?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(19)
这是该问题的 Linq 解决方案。
Here is a Linq solution to the problem.
据我所知,没有任何内置方法可以获取每个组件。但您可以通过以下一种方式获取它们:
我调整了
Enum
内部执行的操作来生成字符串以代替返回标志。您可以查看反射器中的代码,并且应该或多或少是等效的。适用于值包含多个位的一般用例。扩展方法
GetIndividualFlags()
获取某个类型的所有单独标志。因此包含多个位的值被忽略。There aren't any builtin methods to get each component as far as I know. But here's one way you can get them:
I adapted what
Enum
does internally to generate the string to instead return the flags. You can look at the code in reflector and should be more or less equivalent. Works well for general use cases where there are values which contain multiple bits.The extension method
GetIndividualFlags()
gets all the individual flags for a type. So values containing multiple bits are left out.几年后,有了更多的经验,我对单位值的最终答案(从最低位到最高位)是杰夫·梅尔卡多(Jeff Mercado)内部例程的一个轻微变体:
它似乎有效,尽管我几年前的反对意见,我在这里使用 HasFlag,因为它比使用按位比较更清晰,而且速度差异对于我将要做的任何事情来说都是微不足道的。 (无论如何,他们完全有可能从那时起就提高了 HasFlags 的速度,据我所知......我还没有测试过。)
Coming back at this a few years later, with a bit more experience, my ultimate answer for single-bit values only, moving from lowest bit to highest bit, is a slight variant of Jeff Mercado's inner routine:
It seems to work, and despite my objections of some years ago, I use HasFlag here, since it's far more legible than using bitwise comparisons and the speed difference is insignificant for anything I'll be doing. (It's entirely possible they've improved the speed of HasFlags since then anyway, for all I know...I haven't tested.)
脱离 @Greg 的方法,但添加了 C# 7.3 中的新功能,即
Enum
约束:新约束允许这是一个扩展方法,而不必通过
(int)( object)e
,我可以使用HasFlag
方法并从value
直接转换为T
。C# 7.3 还添加了对委托和非托管的约束。
Going off of @Greg's method, but adding a new feature from C# 7.3, the
Enum
constraint:The new constraint allows this to be an extension method, without having to cast through
(int)(object)e
, and I can use theHasFlag
method and cast directly toT
fromvalue
.C# 7.3 also added constraints for delegates and
unmanaged
.@RobinHood70 提供的答案+1。我发现该方法的通用版本对我来说很方便。
编辑
@AustinWBryan +1 将 C# 7.3 引入解决方案空间。
+1 for the answer provided by @RobinHood70. I found that a generic version of the method was convenient for me.
EDIT
And +1 for @AustinWBryan for bringing C# 7.3 into the solution space.
使用新的 Enum 约束和泛型来防止强制转换的扩展方法:
Extension method using the new Enum constraint and generics to prevent casting:
我继续努力缩短代码,这是我的例程的最新版本。 (我是OP……长话短说。)如前所述,这会忽略 None 和多位值。
请注意,这使用 Enum 约束和 var 模式,因此至少需要 C# 7.3。
Continuing in my efforts to make the code shorter, this is my latest version of the routine. (I'm the OP...long story.) As discussed previously, this ignores None and multi-bit values.
Note that this uses an Enum constraint and a var pattern, so will require at least C# 7.3.
这是另一个 C# 7.3 解决方案,使用 Linq
Edit: 添加了
.ToArray()
来防止多次枚举。Here's yet another C# 7.3 solution using Linq
Edit: added
.ToArray()
to prevent multiple enumerations.对上面的答案并不满意,尽管它们只是开始。
在此处拼凑了一些不同的来源之后:实用程序
此线程的 SO QnA 中的上一张海报
代码项目枚举标志检查帖子
伟大的枚举
我创建了这个,所以请让我知道您的想法。
参数:
bool checkZero
:告诉它允许0
作为标志值。默认情况下input = 0
返回空。bool checkFlags
:告诉它检查Enum
是否用[Flags]
属性修饰。附言。我现在没有时间找出
checkCombinators = false
alg,这将迫使它忽略任何位组合的枚举值。这利用了 Helper 类 Enum;发现这里 我更新为对
GetValues
使用yield return
:最后,这是一个使用它的示例:
Wasn't satisfied with the answers above, although they were the start.
After piecing together some different sources here:
Previous poster in this thread's SO QnA
Code Project Enum Flags Check Post
Great Enum<T> Utility
I created this so let me know what you think.
Parameters:
bool checkZero
: tells it to allow0
as a flag value. By defaultinput = 0
returns empty.bool checkFlags
: tells it to check whether theEnum
is decorated w/ the[Flags]
attribute.PS. I don't have time right now to figure out the
checkCombinators = false
alg which will force it to ignore any enum values which are combinations of bits.This makes use of the Helper Class Enum<T> found here that I updated to use
yield return
forGetValues
:Finally, here's a example of using it:
我所做的是改变我的方法,我没有将方法的输入参数输入为
enum
类型,而是将其输入为enum
类型的数组 (MyEnum[] myEnums
),这样我只需在循环内使用 switch 语句迭代数组。What I did was change my approach, instead of typing the input parameter of the method as the
enum
type, I typed it as an array of theenum
type (MyEnum[] myEnums
), this way I just iterate through the array with a switch statement inside the loop.当谈到游戏开发中非常重要的性能时,由于垃圾分配和速度慢,给出的所有解决方案都很糟糕。这是一个无垃圾的解决方案,比公认的答案快 100 倍以上。
When it comes to performance, which is important in game development, all solutions given are terrible because of garbage allocation and slow speed. This is a garbage free, over 100 times faster solution than accepted answer.
在.NET 6中
In .NET 6
您不需要迭代所有值。只需检查您的特定标志,如下所示:
或者(如评论中所述)您可以检查是否使用它,例如:
You dont need to iterate all values. just check your specific flags like so:
or (as pstrjds said in comments) you can check for use it like:
基于上面 Greg 的答案,这也处理了枚举中值为 0 的情况,例如 None = 0。在这种情况下,它不应该迭代该值。
有谁知道如何进一步改进这一点,以便它可以处理以超级智能方式设置枚举中的所有标志的情况,可以处理所有底层枚举类型以及 All = ~0 和 All = EnumValue1 | 的情况枚举值2 |枚举值3 | ...
Building upon Greg's answer above, this also takes care of the case where you have a value 0 in your enum, such as None = 0. In which case, it should not iterate over that value.
Would anyone know how to improve upon this even further so that it can handle the case where all flags in the enum are set in a super smart way that could handle all underlying enum type and the case of All = ~0 and All = EnumValue1 | EnumValue2 | EnumValue3 | ...
所有答案都适用于简单的标志,当标志组合时您可能会遇到问题。
可能需要添加一个检查来测试枚举值本身是否是一个组合。
您可能需要类似
All the answers work well with simple flags, you're probably going to get into issues when flags are combined.
probably need to add a check to test if the enum value it self is a combination.
You'd probably need something like posted here by Henk van Boeijen
to cover your requirement (you need to scroll down a bit)
您可以使用枚举中的迭代器。从 MSDN 代码开始:
它没有经过测试,所以如果我犯了错误,请随时给我打电话。 (显然它不是可重入的。)您必须事先分配值,或者将其分解为另一个使用 enum.dayflag 和 enum.days 的函数。你也许可以根据大纲去某个地方。
You can use an Iterator from the Enum. Starting from the MSDN code:
It's not tested, so if I made a mistake feel free to call me out. (obviously it's not re-entrant.) You'd have to assign value beforehand, or break it out into another function that uses enum.dayflag and enum.days. You might be able to go somewhere with the outline.
它也可以是下面的代码:
只有当枚举值包含在值中时,它才会进入内部
It could be aswell as the following code:
It goes inside if only when the enum value is contained on the value
您可以直接通过转换为 int 来完成此操作,但您将失去类型检查。
我认为最好的方法是使用与我的主张类似的东西。它始终保持正确的类型。无需转换。它并不完美,因为拳击会对性能造成一点影响。
不完美(拳击),但它在没有警告的情况下完成了工作......
用法:
You can do it directly by converting to int but you will loose type checking.
I think the best way is use something similar to my proposition. It keep the proper type all the way. No conversion required. It is not perfect due to boxing which will add a little hit in performance.
Not perfect (boxing), but it does the job with no warning...
Usage: