Counting the number of flags set on an enumeration

2020-07-02 08:24发布

I'm sure there must be a much better way of doing this. I'm trying to do a count operation on a Flags enum. Before I was itterating over all the possible values and counting the succesful AND operations.

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

I'm sure there must be a better way of doing this though, but must be suffering from a mental block. Can anyone advise a nicer solution?

标签: c# enums
9条回答
孤傲高冷的网名
2楼-- · 2020-07-02 08:59

After looking on the site Assaf suggested I managed to find a slightly different solution that I got working for Int32's.

Here's the code for anyone else:

    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;
    }
查看更多
▲ chillily
3楼-- · 2020-07-02 09:03

A very concise way to do it using BitArray and LINQ:

public static int Count(Skills skillsToCount)
{
    return new BitArray(new[] {(int)skillsToCount}).OfType<bool>().Count(x => x);
}
查看更多
疯言疯语
4楼-- · 2020-07-02 09:05

If you're targeting .NET Core 3.0 or above, you can use BitOperations.PopCount(), it operates in uint or ulong and returns the number of 1 bits.

If your CPU supports SSE4, it'll use the POPCNT CPU instruction, otherwise it'll use a software fallback.

public static int Count(Skills skillsToCount)
{
   return BitOperations.PopCount((ulong)skillsToCount);
}
查看更多
三岁会撩人
5楼-- · 2020-07-02 09:06
int count = Enum.GetValues(typeof(Skills)).Length;
查看更多
放我归山
6楼-- · 2020-07-02 09:11

the only reason to use this method is if the flags are not contiguous and if flags will be added periodically.

<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

if "better" means faster this ain't ;) it.

查看更多
够拽才男人
7楼-- · 2020-07-02 09:12

The count is equivalent to counting how many bits are set to 1 in the integer value of the enum.

There are very fast ways of doing this in C/C++, which you can adapt to 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;
}

Taken from here.

EDIT
Provided link is dead. Found another one that probably contains the same content.

查看更多
登录 后发表回答