计算枚举上设置的标志数量

发布于 2024-07-15 20:22:18 字数 637 浏览 3 评论 0原文

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

例如,

[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 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

梦里寻她 2024-07-22 20:22:18

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

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

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

The following code will give you the number of bits that are set for a given number of any type varying in size from byte up to long.

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

This code is very efficient as it only iterates once for each bit rather than once for every possible bit as in the other examples.

魔法唧唧 2024-07-22 20:22:18

在查看 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;
    }

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;
    }
甜宝宝 2024-07-22 20:22:18

使用 BitArray 和 LINQ 的一种非常简洁的方法:

public static int Count(Skills skillsToCount)
{
    return new BitArray(new[] {(int)skillsToCount}).OfType<bool>().Count(x => x);
}

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);
}
冷弦 2024-07-22 20:22:18

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

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

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

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);
}
落花浅忆 2024-07-22 20:22:18

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

在 C/C++ 中有非常快速的方法可以做到这一点,您可以将其适应 C#:

例如

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

取自 此处

编辑
提供的链接已失效。 发现另一个可能包含相同的内容。

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.

美人如玉 2024-07-22 20:22:18

有一种使用函数式编程 (LINQ) 的直接方法:

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

它可能比位处理解决方案慢一点,但它具有恒定的运行时间并且更直观。

虽然 GetValues 仍在分配,但编译器很可能会对其进行优化。

There's a straight-forward way using functional programming (LINQ):

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

It might be a bit slower than the bit-juggling solutions but it has a constant run-time and is more intuitive.

While GetValues still allocates, there is a good chance that the compiler optimizes this away.

她如夕阳 2024-07-22 20:22:18
<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。这对于整数、长整型及其无符号亲属的工作方式相同。

<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

depends on the definition of "better".

the check for Skills.None is required because if no bits are on, the string() returns Skills.None which results in a count of 1. this would work the same for integer, long, and their unsigned relatives.

习惯成性 2024-07-22 20:22:18

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

<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

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

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.

浴红衣 2024-07-22 20:22:18
int count = Enum.GetValues(typeof(Skills)).Length;
int count = Enum.GetValues(typeof(Skills)).Length;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文