2016-06-13 144 views
1

我已经定义了这样的一个枚举:有没有办法判断一个枚举是否只有一个,多个或不设置标志?

[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; 
+2

这看起来不对。你真的可以有一个既是北方又是南方的方向? –

+0

@JonathanAllen是的,不要想一个我可以花一点时间的方向。想想哪些方向可用。可能是“方向”在这里是错误的措词;) – Norman

+1

@ Dennis_E不是真的,因为你可以拥有像'(Orientation)1023'这样的东西,当只有8个标志时它有10个位。 – juharr

回答

3

您可以使用此代码:

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); 
+1

甚至可能是一行Linq'int i = Enum.GetValues(typeof(Orientation))。Cast ().Where(o => item.HasFlag(o))。Count();' – juharr

+0

想到有一个比行走一个循环或使用LINQ更短的方式,但显然没有... – Norman

+0

我选择这一个作为解决方案,但我实际上使用juharr的方法(这在技术上是识别性的)。这似乎是最合理的 - 即使我喜欢Slai用ToString想的方式。 – Norman

1

关闭顶部我的头:

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 
} 
2
var test = Orientation.North; 
var flagCount = GetFlagCount(test); 

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

你正在使用哪个目标框架?在.Net 4.5.2中,一个数组(GetValues)不包含count这个参数的定义... – Norman

+0

对不起,我从我的电话回答,所以没有可能试用它。我已经更新了我的答案。 –

0

您可以将值转换为后,用简单的位伎俩确定这3210:

int v =(int)enumValue;

  1. 如果v == 0,则未设置任何标志
  2. 否则,如果((v-1)&v) == 0,则只有一个标志设置
  3. ,否则,多个标志设置。

唯一棘手的是#2。这是一个解释:考虑一个二进制数字,其中一位设置为1。然后减去1将作出以下修改:

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

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

当有两个或更多个1 s时,位于唯一1左侧的位将保持不变。因此,在按位AND的结果中至少有一个位置将具有1

2

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

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 我个人不会在生产中使用的代码串的方法,需要只是有点计数时尤其如此。

无论如何,我只是为了好玩而想到另一个黑客。当设置一位时,Base 2日志将返回一个完整的数字,当为0时为无穷大,当设置多个位时为其他数据。对于实施例

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的解决方案似乎是最好的。

+1

这是一种很酷的ToString()滥用。你也可以分开逗号。 'foo.ToString()=“Foo,Bizz”' – DVK

+0

我喜欢你的思维方式,而且tostring-split方法非常整齐。但是... ToString在任何平台,任何地区,任何c#版本上的行为是否一致? – Norman

0

这不是计数位数的重复。如果我初始化这样的枚举和计数位,我得到10的数量,但一组标志的数目是8。

请直接from 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); 
+0

var bar =(MyEnum)27在代码中我不会做任何事情。但由于枚举的值以int形式存储在数据库中,因此您可以在不知道枚举的情况下将其更改为任何值。 – Norman

相关问题