【C#】Enum Flagを 高速foreach ...と5bit整数Set



# はじめに
<!-- 発端や概要を記載 -->
C#のenumはFlagAttributeをつけることで、複数のFlagを扱うことができます。
しかし、foreachができなかったり不便なのでC#9.0の機能で作ってみました。
以下パブリックドメインです。
# 目次
<!-- タイトルとアンカー名を編集 -->
1. [使い方](#Chapter1)
2. [実装](#Chapter2)
2. [ついでに](#Chapter3)
<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 使い方
使い方です。
```C#
 [Flags]
private enum Flag:int {
      None=0,
      a=1<<0,
      b=1<<1,
      c=1<<2,
      d=1<<3,
      e=1<<4,
      f=1<<5,
      g=1<<6,
      h=1<<7,
      i=1<<8,
}
public void BitTest() {
     var flag = Flag.a | Flag.d | Flag.g;
     foreach (var f in (int)flag) {
         Debug.Log(((Flag)f).ToString());
}
```

これでちゃんとa,d,gが表示されます。
<a id="#Chapter2"></a>

# 実装
C#9.0からforeachというシンタックスシュガーを使うにはGetEnumeratorでbool MoveNext()とCurrent{get;} を実装したclassかstructを返すだけでよくなりました。

Enumなのでint型を拡張して作ってみます。

```C#
public static Enumerator GetEnumerator(this int bits) {
    return new Enumerator(bits);
}
public  struct Enumerator {
    private  int bits;
    private int count;
    private int position;
    public Enumerator(int bits) {
        this.bits = bits;
        count = NumOfBits(bits);
        position = -1;
    }
    public bool MoveNext() {
        while (0<count) {
            ++position;
            if ((bits& 1)!=0) {
                --count;
                bits >>= 1;
                return true;
            }
            bits >>= 1;
        }
        return false;
    }
    public int Current => (1 << position);
}

```
ここで出てきたNumOfBitsはこんな感じです。
```C#
public static int NumOfBits(int bits)
    {
        bits = (bits & 0x55555555) + (bits >> 1 & 0x55555555);
        bits = (bits & 0x33333333) + (bits >> 2 & 0x33333333);
        bits = (bits & 0x0f0f0f0f) + (bits >> 4 & 0x0f0f0f0f);
        bits = (bits & 0x00ff00ff) + (bits >> 8 & 0x00ff00ff);
        return ((bits & 0x0000ffff) + (bits >> 16 & 0x0000ffff));
    }
```
これはsetされたbitを数える高速なアルゴリズムです。
パフォーマンス的には配列にintを詰め込んだList\<int>　とint[ ]の間くらいになりました。

32bit内でなかなかよくできたと思います。
<a id="#Chapter3"></a>
# ついでに
似た実装で、HashSet<int>とかList<int>は重すぎるので、気軽に使えるように
0~31の整数集合をint32に詰め込んだものです。
```C#
//使用例
public static void BitTest() {
    var s = new SetOfBits32();
    s += 1;
    s += 26;
    s += 3;
    s+=6;
    s-=6;
    foreach (var b in s) {
         Debug.Log(b);//1,3,26
    }
}
//実装
public   struct SetOfBits32  {
    public  int bits { get; private set; }
    public SetOfBits32(int bits) {
        this.bits = bits;
    }
    public static SetOfBits32 operator +(SetOfBits32 left, byte bit) => new SetOfBits32(left.bits | (1 << bit));
        public static SetOfBits32 operator -(SetOfBits32 left, byte bit) => new SetOfBits32(left.bits & ~(1 << bit));

        public static SetOfBits32 operator &(SetOfBits32 left, SetOfBits32 right) =>
            new SetOfBits32(left.bits & right.bits);
        public static SetOfBits32 operator |(SetOfBits32 left, SetOfBits32 right) =>
            new SetOfBits32(left.bits | right.bits);
        public static SetOfBits32 operator ^(SetOfBits32 left, SetOfBits32 right) =>
            new SetOfBits32(left.bits ^ right.bits);
    }
    public bool Contains(byte bit) {
        if (31 <bit) return false;
        return ((1 << bit) & bits)!= 0;
    }
    public int Count=>NumOfBits(bits);
    public  struct Enumerator {
        private  int bits;
        private int count;
        private int position;
        public Enumerator(int bits) {
            this.bits = bits;
            count = NumOfBits(bits);
            position = -1;
        }
        public bool MoveNext() {
            while (0<count) {
                ++position;
                if ((bits& 1)!=0) {
                    --count;
                    bits >>= 1;
                    return true;
                }
                bits >>= 1;
            }
            return false;
        }
        public int Current => position;
    }
    public Enumerator GetEnumerator() {
        return new Enumerator(bits);
    }
}

```
32種類しかないのものなんてたくさんあるので、それなりに使えると思います。
もし void Add(byte bit)とかを実装したい場合はref struct にすればstructであることを意識せずに使いやすいです。

long型(Int64)を使えば、0~63まで扱えるので、必要に応じて使い分けてください。