有没有办法判断一个枚举是否设置了一个、多个或没有标志?
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
}
是否有一种通用的方法来判断是否设置了一个标志、多个标志或 none?
我不关心枚举的值是多少,我只想知道设置了多少个标志。
这不是计算位数的副本。如果我像这样初始化枚举并计算位数,我得到 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 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
}
var test = Orientation.North;
var flagCount = GetFlagCount(test);
public int GetFlagCount(Enum testValue)
{
return Enum.GetValues(testValue.GetType()).Cast<Enum>().Count(testValue.HasFlag);
}
将值转换为 int
:
后,您可以使用简单的位技巧来确定这一点
int v = (int)enumValue;
- 如果
v == 0
,则不设置标志
- 否则,如果
((v-1)&v) == 0
,则正好设置一个标志
- 否则,设置多个标志。
唯一棘手的是#2。这是一个解释:考虑一个二进制数,其中一位正好设置为 1
。然后减去 1
将进行以下更改:
0000010000000
- 1
-------------
0000001111111
单独的 1
之后的所有零变为 1
s,1
变为零,其余位保持不变。 AND
-ing v
和 v-1
产生零,因为在两个数字之间的相同位置没有一对 1
。
当有两个或多个1
时,单独的1
左边的位将保持不变。因此,按位 AND
.
的结果中至少有一个位置会出现 1
如果您正在寻找"shortest"方式:
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();
或者将枚举定义为 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
}
更新 2
我个人不会在生产代码中使用字符串方法,尤其是当只需要一点点计数时。
无论如何,我只是为了好玩而想到了另一个黑客。当设置一位时,基数 2 日志将 return 整数,当设置为 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 的解决方案似乎是最好的。
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.
请直接考虑这个说法from MSDN:
Flags enumerations are used for masking bit fields and doing bitwise comparisons.
如果您设计或使用枚举的方式不允许使用按位运算计算标志的数量,那么您设计或使用枚举的方式不当。
[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);
我有一个这样定义的枚举:
[Flags]
public enum Orientation
{
North = 1,
North_East = 2,
East = 4,
South_East = 8,
South = 16,
South_West = 32,
West = 64,
North_West = 128
}
是否有一种通用的方法来判断是否设置了一个标志、多个标志或 none? 我不关心枚举的值是多少,我只想知道设置了多少个标志。
这不是计算位数的副本。如果我像这样初始化枚举并计算位数,我得到 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 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
}
var test = Orientation.North;
var flagCount = GetFlagCount(test);
public int GetFlagCount(Enum testValue)
{
return Enum.GetValues(testValue.GetType()).Cast<Enum>().Count(testValue.HasFlag);
}
将值转换为 int
:
int v = (int)enumValue;
- 如果
v == 0
,则不设置标志 - 否则,如果
((v-1)&v) == 0
,则正好设置一个标志 - 否则,设置多个标志。
唯一棘手的是#2。这是一个解释:考虑一个二进制数,其中一位正好设置为 1
。然后减去 1
将进行以下更改:
0000010000000
- 1
-------------
0000001111111
单独的 1
之后的所有零变为 1
s,1
变为零,其余位保持不变。 AND
-ing v
和 v-1
产生零,因为在两个数字之间的相同位置没有一对 1
。
当有两个或多个1
时,单独的1
左边的位将保持不变。因此,按位 AND
.
1
如果您正在寻找"shortest"方式:
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();
或者将枚举定义为 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
}
更新 2 我个人不会在生产代码中使用字符串方法,尤其是当只需要一点点计数时。
无论如何,我只是为了好玩而想到了另一个黑客。当设置一位时,基数 2 日志将 return 整数,当设置为 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 的解决方案似乎是最好的。
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.
请直接考虑这个说法from MSDN:
Flags enumerations are used for masking bit fields and doing bitwise comparisons.
如果您设计或使用枚举的方式不允许使用按位运算计算标志的数量,那么您设计或使用枚举的方式不当。
[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);