繁体   English   中英

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

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

我有一个这样定义的枚举:

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

有没有一种通用的方法来确定是设置一个标志,设置多个标志还是不设置标志? 我不在乎枚举的值是什么,我只想知道设置了多少个标志。

这不是重复的位数计数。 如果我这样初始化枚举并计算位数,我将得到10。但是设置标志的数量将是8。

GeographicOrientation go = (GeographicOrientation) 1023;

您可以使用以下代码:

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

如果您正在寻找“最短”的方式:

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

甚至更短:

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

更新资料

要支持大于255的值,您可以使用以下任何丑陋的技巧:

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

或者只是将枚举定义为字节:

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

更新2我个人不会在生产代码中使用字符串方法,尤其是在只需要一点计数的时候。

无论如何,我只是想到了另一个乐趣。 设置一位时,以2为底的日志将返回整数;设置为0时,-Infinity将返回整数;如果设置一位以上,则以其他形式返回。 例如

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

因此,可以使用(byte)go != 0来检查是否设置了任何标志,然后使用Math.Log((byte)go, 2) % 1 == 0来检查是否仅设置了一个标志。

但是,dasblinkenlight的解决方案似乎是最好的。

从我的头顶上:

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
}

将值转换为int之后,可以用简单的技巧来确定:

int v =(int)枚举值;

  1. 如果v == 0 ,则不设置标志
  2. 否则,如果((v-1)&v) == 0 ,则仅设置一个标志
  3. 否则,将设置多个标志。

唯一棘手的是#2。 这是一个解释:考虑一个二进制数,将一位精确地设置为1 然后减去1将进行以下更改:

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

孤行1之后的所有零变为1 s, 1变为零,其余位保持不变。 AND -ing vv-1产生零,因为两个数字之间在相同位置没有一对1

当存在两个或多个1 s时,孤岛1左侧的位将保持不变。 因此,按位AND的结果至少有一个位置为1

这不是重复的位数计数。 如果我这样初始化枚举并计算位数,我将得到10。但是设置标志的数量将是8。

请直接从MSDN考虑以下声明:

标志枚举用于屏蔽位字段并进行按位比较

如果您以某种不允许按位运算计数标志数的方式设计或使用枚举,则说明您在设计或使用枚举时使用不正确。

    [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

如果您正确地设计了枚举,则可以对标志进行计数,如果设置了多个标志,则可以选择短路,因为继续计数将毫无意义。

        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