フラグ列挙型が通常16進値で定義されるのはなぜですか


121

16進数値を使用するフラグ列挙型宣言がよく見られます。例えば:

[Flags]
public enum MyEnum
{
    None  = 0x0,
    Flag1 = 0x1,
    Flag2 = 0x2,
    Flag3 = 0x4,
    Flag4 = 0x8,
    Flag5 = 0x10
}

列挙型を宣言するとき、通常は次のように宣言します。

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1,
    Flag2 = 2,
    Flag3 = 4,
    Flag4 = 8,
    Flag5 = 16
}

一部の人々が10進数ではなく16進数で値を書き込むことを選択する理由または根拠はありますか?私の見たところ、16進数の値を使用すると混乱して、誤ってのFlag5 = 0x16代わりに書き込む方が簡単ですFlag5 = 0x10


4
10進数を使用する場合10よりも0x10、作成する可能性が低くなるのはなぜですか。特にこれらは私たちが扱っている2進数であり、16進数は2進数に簡単に変換できますか? 0x111頭の中で翻訳する方がずっと煩わしくあり273ません...
cHao

4
C#に、2の累乗を明示的に記述する必要のない構文がないのは残念です。
大佐パニック

ここで無意味なことをしています。フラグの背後にある意図は、それらがビットごとに結合されることです。しかし、ビットごとの組み合わせは型の要素ではありません。値Flag1 | Flag2は3であり、3はのドメイン値に対応していませんMyEnum
Kaz

どこで見ますか?リフレクター付き?
giammin

@giammin特定の実装についてではなく、一般的な質問です。たとえば、オープンソースプロジェクトを使用することも、ネットで利用可能なコードだけを使用することもできます。
Adi Lester

回答:


183

理論的根拠は異なるかもしれませんが、私が見る利点は、16進数があなたに思い出させるということです。そのルールに従って遊ぶつもりだ。」16進数は、データのメモリレイアウトが重要な比較的低レベルのトピックを扱っている場合を除いて、めったに使用されません。これを使用すると、それが現在の状況であることがわかります。

また、C#についてはわかりませんが、Cではx << yコンパイル時の定数が有効であることを知っています。ビットシフトの使用が最も明確なようです。

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1 << 0,
    Flag2 = 1 << 1,
    Flag3 = 1 << 2,
    Flag4 = 1 << 3,
    Flag5 = 1 << 4
}

7
これは非常に興味深いことであり、実際には有効なC#列挙型でもあります。
Adi Lester

13
この表記で+1
Sergey Berezovskiy

1
@AllonGuralnek:[Flags]アノテーションを指定すると、コンパイラは一意のビット位置を割り当てますか?通常、0から始まり、1ずつ増加するため、3(10進数)が割り当てられた列挙値は2進数で11になり、2ビットが設定されます。
エリックJ.

2
@エリック:ええと、なぜかはわかりませんが、2の累乗の値が割り当てられることは常に確実でした。たった今チェックしたところ、間違いだったと思います。
Allon Guralnek

38
x << y表記のもう1つの楽しい事実。1 << 10 = KB1 << 20 = MBなど1 << 30 = GB。あなたはちょうど行くことができるバッファのための16キロバイトの配列にしたい場合、それは本当にいいですvar buffer = new byte[16 << 10];
スコット・チェンバレン

43

これらがバイナリフラグであることを簡単に確認できます。

None  = 0x0,  // == 00000
Flag1 = 0x1,  // == 00001
Flag2 = 0x2,  // == 00010
Flag3 = 0x4,  // == 00100
Flag4 = 0x8,  // == 01000
Flag5 = 0x10  // == 10000

けれども進行は、それがより明確になり:

Flag6 = 0x20  // == 00100000
Flag7 = 0x40  // == 01000000
Flag8 = 0x80  // == 10000000

3
私は実際に0x1、0x2、0x4、0x8の前に0を追加します...したがって、0x01、0x02、0x04、0x08、0x10を取得します...読みやすいと思います。私は何かを台無しにしていますか?
LightStriker

4
@Light-まったくありません。これは非常に一般的であるため、これらがどのように調整されるかを確認できます。ビットをより明確にするだけです:)
Oded

2
@LightStrikerだけではそこにそれをスローするつもりはありませんあなたは六角を使用していない場合は問題。ゼロのみで始まる値は、8進数として解釈されます。そう012です10
Jonathon Reinhart

@JonathonRainhart:私が知っていること。しかし、ビットフィールドを使用するときは常に16進数を使用します。int代わりに安全に使用できるとは思いません。ばかげていることは知っていますが、習慣は大変なものです。
LightStriker、2012年

36

シーケンスが常に1、2、4、8であり、次に0を追加するからだと思い
ます。ご覧のとおり:

0x1 = 1 
0x2 = 2
0x4 = 4
0x8 = 8
0x10 = 16
0x20 = 32
0x40 = 64
0x80 = 128
0x100 = 256
0x200 = 512
0x400 = 1024
0x800 = 2048

同様に、シーケンス1-2-4-8を覚えている限り、2の累乗を覚えていなくても、後続のすべてのフラグを作成できます。


13

なぜなら[Flags]、列挙型は実際にはビットフィールドです。では[Flags]、ビットごとのAND(&)およびOR(|)演算子を使用してフラグを組み合わせることができます。このようなバイナリ値を処理する場合、16進数値を使用する方がほとんどの場合より明確です。これが、そもそも16進数を使用する理由です。各16進文字は、正確に 1ニブル(4ビット)に対応します。10進数では、この1対4のマッピングは当てはまりません。


2
ビット演算を使用する機能は、実際にはflags属性とは関係ありません。
Mattias Nordqvist

4

ヘクスで2のべき乗を2倍にする機械的で簡単な方法があるからです。10進数では、これは難しいです。それはあなたの頭の中で長い乗算を必要とします。16進数では単純な変更です。これ1UL << 63は、10進数では実行できないところまで実行できます。


1
これは最も理にかなっていると思います。値のセットが大きい列挙型の場合、これが最も簡単な方法です(@Hypercubeの例を除いて)。
Adi Lester

3

ビットがフラグにある人間にとっては従うのが簡単だからです。各16進数は4ビットのバイナリに適合します。

0x0 = 0000
0x1 = 0001
0x2 = 0010
0x3 = 0011

... and so on

0xF = 1111

通常、フラグがビットとオーバーラップしないようにする必要があります。フラグを宣言して視覚化する最も簡単な方法は、16進値を使用してフラグを宣言することです。

したがって、16ビットのフラグが必要な場合は、4桁の16進値を使用することで、誤った値を回避できます。

0x0001 //= 1 = 000000000000 0001
0x0002 //= 2 = 000000000000 0010
0x0004 //= 4 = 000000000000 0100
0x0008 //= 8 = 000000000000 1000
...
0x0010 //= 16 = 0000 0000 0001 0000
0x0020 //= 32 = 0000 0000 0010 0000
...
0x8000 //= 32768 = 1000 0000 0000 0000

この説明は適切です。不足しているのは、ビット、ニブル、およびバイトの最終的なラインナップを示すバイナリと同等のものだけです;)
GoldBishop '31年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.