简体   繁体   English

有没有办法判断一个枚举是否只设置了一个,多个或没有标志?

[英]Is there a way to tell if an enum has exactly one, multiple or no flags set?

I have an enum defined like that: 我有一个这样定义的枚举:

[Flags]
public enum Orientation
{
    North = 1,
    North_East = 2,
    East = 4,
    South_East = 8,
    South = 16,
    South_West = 32,
    West = 64,
    North_West = 128
}

Is there a generic way to tell if exactly one flag is set, multiple or none? 有没有一种通用的方法来确定是设置一个标志,设置多个标志还是不设置标志? I don't care for what the value of the enum is, I just want to know how many flags are set. 我不在乎枚举的值是什么,我只想知道设置了多少个标志。

This is not a duplicate for counting the number of bits. 这不是重复的位数计数。 If I initialize the enum like this and count the number of bits I get 10. But the number of set flags would be 8. 如果我这样初始化枚举并计算位数,我将得到10。但是设置标志的数量将是8。

GeographicOrientation go = (GeographicOrientation) 1023;

you can use this code : 您可以使用以下代码:

var item = Orientation.North | Orientation.South;
int i = 0;
foreach (Orientation e in Enum.GetValues(typeof(Orientation)))
    if(item.HasFlag(e))
        i++;

Console.WriteLine(i);
var test = Orientation.North;
var flagCount = GetFlagCount(test);

public int GetFlagCount(Enum testValue)
{
    return Enum.GetValues(testValue.GetType()).Cast<Enum>().Count(testValue.HasFlag);
}

If you are looking for the "shortest" way: 如果您正在寻找“最短”的方式:

Orientation o = Orientation.East | Orientation.West;   // o.ToString() = "East, West"
var c = o.ToString().Split().Count(); 

or even shorter: 甚至更短:

var c = (o + "").Split().Count(); 

Update 更新资料

To support values above 255, you can use any of those ugly hacks: 要支持大于255的值,您可以使用以下任何丑陋的技巧:

Orientation o = (Orientation) 1023;   
var c = ((Orientation)(byte)o + "").Split().Count();
c = ((Orientation)((int)o & 255) + "").Split().Count();

or just define the enum as byte: 或者只是将枚举定义为字节:

    [Flags]
    public enum Orientation : byte
    {
        North = 1,
        North_East = 2,
        East = 4,
        South_East = 8,
        South = 16,
        South_West = 32,
        West = 64,
        North_West = 128
    }

Update 2 I personally wouldn't use the string method in production code especially when just a bit count is needed. 更新2我个人不会在生产代码中使用字符串方法,尤其是在只需要一点计数的时候。

Anyway, I just thought of another hack just for fun. 无论如何,我只是想到了另一个乐趣。 Base 2 log will return a whole number when one bit is set, -Infinity when 0, and anything else when more than one bit is set. 设置一位时,以2为底的日志将返回整数;设置为0时,-Infinity将返回整数;如果设置一位以上,则以其他形式返回。 For Example 例如

 Math.Log(0, 2 ) // -Infinity
 Math.Log(0, 64) // 6.0
 Math.Log(0, 65) // 6.0223678130284544

So, (byte)go != 0 can be used to check if any flags are set, and then Math.Log((byte)go, 2) % 1 == 0 to check if only one flag is set. 因此,可以使用(byte)go != 0来检查是否设置了任何标志,然后使用Math.Log((byte)go, 2) % 1 == 0来检查是否仅设置了一个标志。

But, dasblinkenlight's solution seems like the best. 但是,dasblinkenlight的解决方案似乎是最好的。

Off the top of my head: 从我的头顶上:

var map = 1;
var count = 0;

while (map <= North_West)
{
    if( ((int)Value & map) > 0) 
       count += 1; 
    map = map << 1; //left shift, a.k.a. multiply by 2
}

You can determine this with a simple bit trick after converting the value to int : 将值转换为int之后,可以用简单的技巧来确定:

int v = (int)enumValue; int v =(int)枚举值;

  1. If v == 0 , then no flags are set 如果v == 0 ,则不设置标志
  2. Otherwise, if ((v-1)&v) == 0 , then exactly one flag is set 否则,如果((v-1)&v) == 0 ,则仅设置一个标志
  3. Otherwise, multiple flags are set. 否则,将设置多个标志。

The only tricky one is #2. 唯一棘手的是#2。 Here is an explanation: consider a binary number with exactly one bit set to 1 . 这是一个解释:考虑一个二进制数,将一位精确地设置为1 Then subtracting 1 would make the following change: 然后减去1将进行以下更改:

  0000010000000
-             1
  -------------
  0000001111111

All zeros following the lone 1 become 1 s, 1 becomes zero, and the rest of the bits remain the same. 孤行1之后的所有零变为1 s, 1变为零,其余位保持不变。 AND -ing v and v-1 produces zero, because there is no pair of 1 s in the same position between the two numbers. AND -ing vv-1产生零,因为两个数字之间在相同位置没有一对1

When there are two or more 1 s, the bits to the left of the lone 1 will remain unchanged. 当存在两个或多个1 s时,孤岛1左侧的位将保持不变。 Therefore, at least one position will have a 1 in the result of bitwise AND . 因此,按位AND的结果至少有一个位置为1

This is not a duplicate for counting the number of bits. 这不是重复的位数计数。 If I initialize the enum like this and count the number of bits I get 10. But the number of set flags would be 8. 如果我这样初始化枚举并计算位数,我将得到10。但是设置标志的数量将是8。

Please consider this statement directly from MSDN : 请直接从MSDN考虑以下声明:

Flags enumerations are used for masking bit fields and doing bitwise comparisons . 标志枚举用于屏蔽位字段并进行按位比较

If you are designing or using the enum in some manner that does NOT allow counting the number of flags using bitwise operations, then you are designing or using the enum improperly. 如果您以某种不允许按位运算计数标志数的方式设计或使用枚举,则说明您在设计或使用枚举时使用不正确。

    [Flags]
    public enum MyEnum
    {
        Foo = 1,
        Bar = 2,
        Bizz = 4,
        Buzz = 8,
        Boo = 16
    }


var foo = MyEnum.Foo | MyEnum.Foo | MyEnum.Bizz;
// foo == MyEnum.Foo | MyEnum.Bizz because setting the same flag twice does nothing

var bar = (MyEnum)27 // Some invalid combination
// bar == 27.  Why would you do this instead of MyEnum.Boo | MyEnum.Buzz | MyEnum.Bar | MyEnum.Foo

If you design your enum properly, you can count the flag, and optionally short circuit if there is more than one flag set since continuing to count would be pointless. 如果您正确地设计了枚举,则可以对标志进行计数,如果设置了多个标志,则可以选择短路,因为继续计数将毫无意义。

        var foo = MyEnum.Foo | MyEnum.Bizz;
        int fooValue = (int)foo;
        int numberOfFlags = 0;

        while (fooValue > 0 && numberOfFlags < 2) // Stop counting if more than one flag since we don't need exact count
        {
            if ((fooValue & 1) == 1)
            {
                numberOfFlags++;
            }

            fooValue >>= 1;
        }

        Console.WriteLine(numberOfFlags);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM