[英]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)枚举值;
v == 0
,则不设置标志 ((v-1)&v) == 0
,则仅设置一个标志 唯一棘手的是#2。 这是一个解释:考虑一个二进制数,将一位精确地设置为1
。 然后减去1
将进行以下更改:
0000010000000
- 1
-------------
0000001111111
孤行1
之后的所有零变为1
s, 1
变为零,其余位保持不变。 AND
-ing v
和v-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.