找到标志枚举长度的有效方法?

发布于 2024-08-02 17:42:01 字数 1039 浏览 5 评论 0原文

考虑一下:

[Flags]
enum Colors
{
    Red=1,
    Green=2,
    Blue=4
}

Colors myColor=Colors.Red|Colors.Blue;

目前,我正在这样做:

int length=myColors.ToString().Split(new char[]{','}).Length;

但我希望有一种更有效的方法来查找长度,也许基于位集操作。

如果可能,请解释您的解决方案的工作原理和原因。

另外,如果这是重复的,请指出它,我将删除这个问题。我能找到的唯一类似的问题是关于查找 Colors 枚举的所有可能组合的长度,而不是 myColors 变量的长度。

更新:我仔细对每个解决方案进行了基准测试(每个解决方案 1 000 000 次迭代),结果如下:

  1. Stevo3000 - 8ms
  2. MattEvans - 10ms
  3. Silky - 34ms
  4. Luke - 1757ms
  5. Guffa - 4226ms
  6. Tomas Levesque - 32810ms

Stevo3000 是明显的赢家(Matt Evans 持有)银牌)。

非常感谢您的帮助。

更新2: 该解决方案运行速度更快:100 000 000 次迭代仅需 41 毫秒(比 Stevo3000 快大约 40 倍(32 位操作系统))

UInt32 v = (UInt32)co;
v = v - ((v >> 1) & 0x55555555); 
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 
UInt32 count = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; 

Consider this:

[Flags]
enum Colors
{
    Red=1,
    Green=2,
    Blue=4
}

Colors myColor=Colors.Red|Colors.Blue;

Currently, I'm doing it as follows:

int length=myColors.ToString().Split(new char[]{','}).Length;

But I hope there is a more efficient way of finding the length, maybe based on bitset operations.

Please, if possible, provide explanation why and how your solution works.

Also, if this a duplicate, please point to it and I'll delete this question. The only similar questions on SO I've been able to find were concerned about finding the length of all possible combinations of Colors enum, but not of the myColors variable.

UPDATE: I carefully benchmarked every solution (1 000 000 iterations each) and here is the results:

  1. Stevo3000 - 8ms
  2. MattEvans - 10ms
  3. Silky - 34ms
  4. Luke - 1757ms
  5. Guffa - 4226ms
  6. Tomas Levesque - 32810ms

The Stevo3000 is a clear winner (with Matt Evans holding silver medal).

Thank you very much for your help.

UPDATE 2:
This solution runs even faster: 41 ms for 100 000 000 iterations (roughly 40 times faster (32bit OS) than Stevo3000)

UInt32 v = (UInt32)co;
v = v - ((v >> 1) & 0x55555555); 
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 
UInt32 count = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; 

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

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

发布评论

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

评论(10

蓝戈者 2024-08-09 17:42:02

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

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-08-09 17:42:02

以下是一些操作 Flags 枚举的扩展方法:

public static class EnumExtensions
{
    private static void CheckEnumWithFlags<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
        if (!Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
            throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
    }

    public static bool IsFlagSet<T>(this T value, T flag) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flag);
        return (lValue & lFlag) != 0;
    }

    public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
    {
        CheckEnumWithFlags<T>();
        foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
        {
            if (value.IsFlagSet(flag))
                yield return flag;
        }
    }

    public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flags);
        if (on)
        {
            lValue |= lFlag;
        }
        else
        {
            lValue &= (~lFlag);
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static T SetFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, true);
    }

    public static T ClearFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, false);
    }

    public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = 0;
        foreach (T flag in flags)
        {
            long lFlag = Convert.ToInt64(flag);
            lValue |= lFlag;
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }
}

在您的情况下,您可以使用 GetFlags 方法:

int count = myColors.GetFlags().Count();

它可能不如 Luke 的答案那么有效,但它更容易使用。 ..

Here are a few extension methods to manipulate Flags enumerations :

public static class EnumExtensions
{
    private static void CheckEnumWithFlags<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
        if (!Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
            throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
    }

    public static bool IsFlagSet<T>(this T value, T flag) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flag);
        return (lValue & lFlag) != 0;
    }

    public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
    {
        CheckEnumWithFlags<T>();
        foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
        {
            if (value.IsFlagSet(flag))
                yield return flag;
        }
    }

    public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flags);
        if (on)
        {
            lValue |= lFlag;
        }
        else
        {
            lValue &= (~lFlag);
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static T SetFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, true);
    }

    public static T ClearFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, false);
    }

    public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = 0;
        foreach (T flag in flags)
        {
            long lFlag = Convert.ToInt64(flag);
            lValue |= lFlag;
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }
}

In your case you can use the GetFlags method :

int count = myColors.GetFlags().Count();

It's probably not as efficient as Luke's answer, but it's easier to use...

倒数 2024-08-09 17:42:02

这是我对此的看法...它计算值中设置的位数

int val = (int)myColor;
int count = 0;

while (val > 0)
{
    if((val & 1) != 0)
    {
        count++;
    }

    val = val >> 1;
}

Here's my take on this... it counts the number of set bits in the value

int val = (int)myColor;
int count = 0;

while (val > 0)
{
    if((val & 1) != 0)
    {
        count++;
    }

    val = val >> 1;
}
清眉祭 2024-08-09 17:42:02

这是一种计算位数的相当简单的方法。每个位依次移位到 Int64 的 LSB,该 LSB 与 1 进行 AND 运算(以屏蔽任何其他位),然后添加到运行中全部的。

int length = Enumerable.Range(0, 64).Sum(x => ((long)myColor >> x) & 1);

Here's a reasonably easy way of counting the bits. Each bit is shifted in-turn to the LSB of an Int64 which is AND-ed with 1 (to mask out any of the other bits) and then added to the running total.

int length = Enumerable.Range(0, 64).Sum(x => ((long)myColor >> x) & 1);
甜心小果奶 2024-08-09 17:42:02

粗略的近似值只是计算 myColors 中设置的位数,但这仅在每个枚举成员的值都是 2 的幂时才有效。

A rough approximation will be just counting the number of bits set in myColors, but that will only work if every enumeration members' value is power of 2.

梦醒灬来后我 2024-08-09 17:42:02

假设它们是标志,您可以使用此处的方法之一,计算设置的位数。

它之所以有效,是因为只要它们是标志,当每个标志被“或”时,它就会设置一位。

--

使用该链接上的方法之一编辑示例代码:

[Flags]
enum Test
{
    F1 = 1,
    F2 = 2,
    F3 = 4
}


class Program
{
    static void Main(string[] args)
    {
        int v = (int) (Test.F1 | Test.F2 | Test.F3); // count bits set in this (32-bit value)
        int c = 0; // store the total here
        int[] S = {1, 2, 4, 8, 16}; // Magic Binary Numbers
        int[] B = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF};

        c = v - ((v >> 1) & B[0]);
        c = ((c >> S[1]) & B[1]) + (c & B[1]);
        c = ((c >> S[2]) + c) & B[2];
        c = ((c >> S[3]) + c) & B[3];
        c = ((c >> S[4]) + c) & B[4];

        Console.WriteLine(c);
        Console.Read();
    }
}

Assuming they are flags, you can just use one of the methods here, to count the number of bits set.

It works because, as long as they are flags, when each one is 'OR'd' on, it sets one bit.

-- Edit

Sample code using one of the methods on that link:

[Flags]
enum Test
{
    F1 = 1,
    F2 = 2,
    F3 = 4
}


class Program
{
    static void Main(string[] args)
    {
        int v = (int) (Test.F1 | Test.F2 | Test.F3); // count bits set in this (32-bit value)
        int c = 0; // store the total here
        int[] S = {1, 2, 4, 8, 16}; // Magic Binary Numbers
        int[] B = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF};

        c = v - ((v >> 1) & B[0]);
        c = ((c >> S[1]) & B[1]) + (c & B[1]);
        c = ((c >> S[2]) + c) & B[2];
        c = ((c >> S[3]) + c) & B[3];
        c = ((c >> S[4]) + c) & B[4];

        Console.WriteLine(c);
        Console.Read();
    }
}
兰花执着 2024-08-09 17:42:02

我为自己制定了一个辅助方法。也许对其他人有用。

public static class EnumHelper 
{
    public static UInt32 NumFlags(this Enum e)
    {
        UInt32 v = Convert.ToUInt32(e);
        v = v - ((v >> 1) & 0x55555555);
        v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
        UInt32 count = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
        return count;
    }
}

I've made a helper method for myself. Maybe it'll be useful for others.

public static class EnumHelper 
{
    public static UInt32 NumFlags(this Enum e)
    {
        UInt32 v = Convert.ToUInt32(e);
        v = v - ((v >> 1) & 0x55555555);
        v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
        UInt32 count = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
        return count;
    }
}
苄①跕圉湢 2024-08-09 17:42:02

最可靠的解决方案是测试枚举中的每个值:

int len = 0;
foreach (Colors color in Enum.GetValues(typeof(Colors))) {
   if ((myColor & color) == color) {
      len++;
   }
}

即使该值在枚举中没有定义值的情况下设置了位,这也将起作用,例如:

Colors myColor = (Colors)65535;

这也适用于具有使用更多值的值的枚举比一位:

[Flags]
enum Colors {
   Red = 0xFF0000,
   Green = 0x00FF00,
   Blue = 0x0000FF
}

The solution that is most reliable is to test for each value in the enumeration:

int len = 0;
foreach (Colors color in Enum.GetValues(typeof(Colors))) {
   if ((myColor & color) == color) {
      len++;
   }
}

This will work even if the value has bits set where there are no defined value in the enumeration, for example:

Colors myColor = (Colors)65535;

This will also work for enumerations with values that use more than a single bit:

[Flags]
enum Colors {
   Red = 0xFF0000,
   Green = 0x00FF00,
   Blue = 0x0000FF
}
北恋 2024-08-09 17:42:02
int value = Enum.GetNames(typeof(Colors)).Length;
public static int NumberOfOptions(int value)
{
    int result = (int)Math.Pow(2, value-1);
    return result;
}
int value = Enum.GetNames(typeof(Colors)).Length;
public static int NumberOfOptions(int value)
{
    int result = (int)Math.Pow(2, value-1);
    return result;
}
情何以堪。 2024-08-09 17:42:02

试试这个……

Colors.GetValues().Length();

还是这太明显了?

编辑:

好的,我刚刚再次阅读了这个问题,并意识到您需要“mycolors”的长度,而不是“Colors”的长度 - 让我考虑一下。

进一步编辑:

现在我很困惑 - OP 发布的解决方案永远不会工作,因为 myColor.ToString() 返回 '5' 并将 Split(new char[]{','}) 应用于此将导致一个带有长度为1。
OP真的让它发挥作用了吗?

Try this...

Colors.GetValues().Length();

...or is that too obvious?

EDIT:

OK, I just read the question again, and realised that you need the length of 'mycolors', not 'Colors' - let me think about that.

FURTHER EDIT:

Now I'm confused - the OP's posted solution would never work, as myColor.ToString() returns '5' and applying Split(new char[]{','}) to this would result in a array with a length of 1.
Did the OP actually get this to work?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文