8

このことを考慮:

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

Colors myColor=Colors.Red|Colors.Blue;

現在、私は次のようにしています:

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

しかし、おそらくビットセット操作に基づいて、長さを見つけるより効率的な方法があることを願っています。

可能であれば、ソリューションが機能する理由と方法を説明してください。

また、これが重複している場合は、それを指摘してください。この質問を削除します。私が見つけることができたSOに関する唯一の同様の質問は、変数Colorsではなく、列挙型のすべての可能な組み合わせの長さを見つけることに関するものでした。myColors

更新: すべてのソリューションを慎重にベンチマークしました (それぞれ 1 000 000 回の反復)。結果は次のとおりです。

  1. Stevo3000 - 8ms
  2. マットエバンス - 10ms
  3. シルキー - 34ms
  4. ルーク - 1757ms
  5. グッファ - 4226ms
  6. トーマス・レベスク - 32810ms

Stevo3000 は明らかな勝者です (Matt Evans が銀メダルを保持しています)。

ご助力ありがとうございます。

更新 2: このソリューションはさらに高速に実行されます: 100 000 000 反復で 41 ミリ秒 (Stevo3000 よりも約 40 倍高速 (32 ビット OS))

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

10 に答える 10

11

次のコードは、バイトからロングまでサイズが異なる任意のタイプの特定の数に設定されるビット数を示します。

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回ではなく、各ビットに対して1回だけ反復するため、非常に効率的です。

于 2009-08-26T07:48:26.103 に答える
3

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();

おそらくルークの答えほど効率的ではありませんが、使いやすいです...

于 2009-08-26T07:54:59.057 に答える
2

これがビットを数えるかなり簡単な方法です。各ビットは順番にLSBにシフトされます。LSBは1Int64AND-edされ(他のビットをマスクするため)、現在の合計に追加されます。

int length = Enumerable.Range(0, 64).Sum(x => ((long)myColor >> x) & 1);
于 2009-08-26T07:42:32.700 に答える
2

これが私の見解です...値のセットビット数をカウントします

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

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

    val = val >> 1;
}
于 2009-08-26T07:51:30.793 に答える
1

それらがフラグであると仮定すると、ここのメソッドの1つを使用して、設定されたビット数をカウントできます。

それらがフラグである限り、それぞれが「OR」されると1ビットが設定されるため、機能します。

- 編集

そのリンクのメソッドの 1 つを使用するサンプル コード:

[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();
    }
}
于 2009-08-26T07:31:56.263 に答える
1

大まかな概算は に設定されたビット数をカウントするだけですmyColorsが、これはすべての列挙メンバーの値が 2 の累乗である場合にのみ機能します。

于 2009-08-26T07:32:11.957 に答える
1

自分用のヘルパーメソッドを作成しました。多分それは他の人に役立つでしょう。

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;
    }
}
于 2013-02-05T17:19:12.140 に答える
0

最も信頼できる解決策は、列挙の各値をテストすることです。

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
}
于 2009-08-26T08:25:26.903 に答える
-3

これを試して...

Colors.GetValues().Length();

...それはあまりにも明白ですか?

編集:

OK、質問をもう一度読んだところ、「Colors」ではなく「mycolors」の長さが必要であることに気付きました。それについて考えさせてください。

さらに編集:

今、私は混乱しています - myColor.ToString() が '5' を返し、これに Split(new char[]{','}) を適用すると、長さが 1 の配列になるため、OP の投稿されたソリューションは機能しません。 OPは実際にこれを機能させましたか?

于 2009-08-26T07:35:59.373 に答える