计算枚举上设置的标志数

时间:2009-03-24 12:41:12

标签: c# enums

我确信必须有更好的方法来做到这一点。我正在尝试对Flags枚举进行计数操作。在我对所有可能的值进行迭代并计算成功的AND操作之前。

e.g。

[Flags]
public enum Skills
{
    None = 0,
    Skill1 = 1,
    Skill2 = 2,
    Skill3 = 4,
    Skill4 = 8,
    Skill5 = 16,
    Skill6 = 32,
    Skill7 = 64,
    Skill8 = 128
}

public static int Count(Skills skillsToCount)
{
   Skills skill;
   for (int i = 0; i < SkillSet.AllSkills.Count; i++)
   {
      skill = SkillSet.AllSkills[i];
      if ((skillsToCount & skill) == skill && skill != Skills.None)
         count++;
   }
   return count;
}

我确信必须有更好的方法来做到这一点,但必须患有精神障碍。任何人都可以建议更好的解决方案吗?

10 个答案:

答案 0 :(得分:28)

以下代码将为您提供为给定数量的任何类型设置的位数,其大小从字节到长度不等。

public static int GetSetBitCount(long lValue)
{
  int iCount = 0;

  //Loop the value while there are still bits
  while (lValue != 0)
  {
    //Remove the end bit
    lValue = lValue & (lValue - 1);

    //Increment the count
    iCount++;
  }

  //Return the count
  return iCount;
}

这段代码非常有效,因为它只针对每个位迭代一次,而不是像其他示例那样针对每个可能的位迭代一次。

答案 1 :(得分:6)

在查看网站后,Assaf建议我找到了一个与Int32相同的解决方案。

以下是其他任何人的代码:

    internal static UInt32 Count(this Skills skills)
    {
        UInt32 v = (UInt32)skills;
        v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
        v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
        UInt32 c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
        return c;
    }

答案 2 :(得分:4)

使用BitArray和LINQ:

这是一种非常简洁的方法
public static int Count(Skills skillsToCount)
{
    return new BitArray(new[] {(int)skillsToCount}).OfType<bool>().Count(x => x);
}

答案 3 :(得分:3)

计数相当于计算枚举的整数值中有多少位设置为1。

在C / C ++中有很快的方法可以适应C#:

e.g。

int bitcount(unsigned int n) {
   /* works for 32-bit numbers only    */
   /* fix last line for 64-bit numbers */

   register unsigned int tmp;

   tmp = n - ((n >> 1) & 033333333333)
           - ((n >> 2) & 011111111111);
   return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}

取自here

修改
提供的链接已经死亡。找到可能包含相同内容的another one

答案 4 :(得分:2)

如果目标是.NET Core 3.0或更高版本,则可以使用BitOperations.PopCount(),它以uintulong进行操作,并返回1位的数量。

如果您的CPU支持SSE4,它将使用POPCNT CPU指令,否则将使用软件后备。

public static int Count(Skills skillsToCount)
{
   return BitOperations.PopCount((ulong)skillsToCount);
}

答案 5 :(得分:0)

<FlagsAttribute()> _
Public Enum Skills As Byte
    None = 0
    Skill1 = 1
    Skill2 = 2
    Skill3 = 4
    Skill4 = 8
    Skill5 = 16
    Skill6 = 32
    Skill7 = 64
    Skill8 = 128
End Enum


    Dim x As Byte = Skills.Skill4 Or Skills.Skill8 Or Skills.Skill6
    Dim count As Integer
    If x = Skills.None Then count = 0 Else _
        count = CType(x, Skills).ToString().Split(New Char() {","c}, StringSplitOptions.RemoveEmptyEntries).Count

取决于“更好”的定义。

检查Skills.None是必需的,因为如果没有打开位,则string()返回Skills.None,这将导致计数为1.这对于整数,long和它们的无符号亲属都是相同的。

答案 6 :(得分:0)

使用此方法的唯一原因是标志是不连续的,是否会定期添加标志。

<FlagsAttribute()> _
Public Enum Skills As Integer
    Skill1 = CInt(2 ^ 0) 'bit 0
    Skill2 = CInt(2 ^ 1)
    Skill3 = CInt(2 ^ 2)
    Skill4 = CInt(2 ^ 3)
    Skill5 = CInt(2 ^ 4)
    Skill6 = CInt(2 ^ 5)
    Skill7 = CInt(2 ^ 6)
    Skill8 = CInt(2 ^ 7)
    Skillx = CInt(2 ^ 10) 'bit 10, some bits were skipped
End Enum


    Dim mySkills As Integer = Skills.Skillx Or Skills.Skill4 Or Skills.Skill8 Or Skills.Skill6
    Dim count As Integer 'count of bits on
    count = CType(mySkills, Skills).ToString().Split(New Char() {","c}, _
                                                     StringSplitOptions.RemoveEmptyEntries).Count

如果“更好”意味着更快,这不是;)它。

答案 7 :(得分:0)

使用函数式编程(LINQ)是一种直截了当的方式:

var skillCount = Enum
    .GetValues(typeof(Skills))
    .Cast<Enum>()
    .Count(skills.HasFlag);

它可能比bit-juggling解决方案慢一点,但它仍然没有分配,运行时间不变,更直观。

答案 8 :(得分:-2)

int count = Enum.GetValues(typeof(Skills)).Length;

答案 9 :(得分:-3)

Enum.GetNames()将返回枚举中所有名称的数组,添加.Length以查找计数。