Warum werden Flaggenaufzählungen normalerweise mit hexadezimalen Werten definiert?

121

Oft sehe ich Flag-Enum-Deklarationen, die hexadezimale Werte verwenden. Beispielsweise:

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

Wenn ich eine Aufzählung deklariere, erkläre ich sie normalerweise so:

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

Gibt es einen Grund oder eine Begründung dafür, warum manche Leute den Wert lieber hexadezimal als dezimal schreiben? So wie ich es sehe, ist es einfacher, verwirrt zu werden, wenn Hex-Werte verwendet werden und versehentlich Flag5 = 0x16statt geschrieben werden Flag5 = 0x10.

Adi Lester
quelle
4
Was würde es weniger wahrscheinlich machen, dass Sie schreiben, 10als 0x10wenn Sie Dezimalzahlen verwenden würden? Zumal es sich um Binärzahlen handelt, mit denen wir es zu tun haben, und hex trivial in / von Binärzahlen konvertierbar ist? 0x111Es ist weit weniger ärgerlich, im Kopf zu übersetzen als 273...
CHA
4
Es ist eine Schande, dass C # keine Syntax hat, die nicht explizit das Ausschreiben der Zweierpotenzen erfordert.
Colonel Panic
Du machst hier etwas Unsinniges. Die Absicht hinter Flags ist, dass sie bitweise kombiniert werden. Die bitweisen Kombinationen sind jedoch keine Elemente des Typs. Der Wert Flag1 | Flag2ist 3 und 3 entspricht keinem Domänenwert von MyEnum.
Kaz
Wo siehst du das? mit Reflektor?
Giammin
@giammin Es ist eine allgemeine Frage, nicht über eine bestimmte Implementierung. Sie können beispielsweise Open Source-Projekte oder nur im Internet verfügbaren Code verwenden.
Adi Lester

Antworten:

183

Rationales mögen unterschiedlich sein, aber ein Vorteil, den ich sehe, ist, dass Sie hexadezimal daran erinnert werden: "Okay, wir haben es nicht mehr mit Zahlen in der willkürlich vom Menschen erfundenen Welt der Basis zehn zu tun. Wir haben es mit Bits zu tun - der Welt der Maschine - und wir Ich werde nach seinen Regeln spielen. " Hexadezimal wird nur selten verwendet, wenn Sie sich mit Themen auf relativ niedriger Ebene befassen, bei denen das Speicherlayout von Daten von Bedeutung ist. Die Verwendung deutet darauf hin, dass dies die Situation ist, in der wir uns gerade befinden.

Ich bin mir auch nicht sicher über C #, aber ich weiß, dass in C x << yeine gültige Konstante für die Kompilierungszeit ist. Die Verwendung von Bitverschiebungen scheint am klarsten zu sein:

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1 << 0,
    Flag2 = 1 << 1,
    Flag3 = 1 << 2,
    Flag4 = 1 << 3,
    Flag5 = 1 << 4
}
existiert für alle
quelle
7
Das ist sehr interessant und es ist auch eine gültige C # -Aufzählung.
Adi Lester
13
+1 mit dieser Notation machen Sie nie Fehler in Enum-Wert-Berechnungen
Sergey Berezovskiy
1
@AllonGuralnek: Weist der Compiler angesichts der Annotation [Flags] eindeutige Bitpositionen zu? Im Allgemeinen beginnt es bei 0 und geht in Schritten von 1 vor, sodass jeder Aufzählungswert, dem 3 (dezimal) zugewiesen ist, binär 11 ist und zwei Bits setzt.
Eric J.
2
@ Eric: Huh, ich weiß nicht warum, aber ich war mir immer sicher, dass es Werte von Zweierpotenzen zuweist. Ich habe gerade nachgesehen und ich glaube, ich habe mich geirrt.
Allon Guralnek
38
Eine weitere lustige Tatsache mit der x << yNotation. 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GBUnd so weiter. Es ist wirklich schön, wenn Sie ein 16-KB-Array für einen Puffer var buffer = new byte[16 << 10];
Scott Chamberlain
43

Es macht es leicht zu erkennen, dass dies binäre Flags sind.

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

Obwohl der Fortschritt es noch deutlicher macht:

Flag6 = 0x20  // == 00100000
Flag7 = 0x40  // == 01000000
Flag8 = 0x80  // == 10000000
Oded
quelle
3
Ich füge tatsächlich eine 0 vor 0x1, 0x2, 0x4, 0x8 hinzu ... Also bekomme ich 0x01, 0x02, 0x04, 0x08 und 0x10 ... ich finde das leichter zu lesen. Vermassle ich etwas?
LightStriker
4
@Light - Überhaupt nicht. Es ist sehr häufig, so dass Sie sehen können, wie diese ausgerichtet sind.
Oded
2
@LightStriker geht nur um es dort zu werfen , das tut ganz gleich , ob Sie nicht hex verwenden. Werte, die nur mit einer Null beginnen, werden als oktal interpretiert. So 012ist es eigentlich 10.
Jonathon Reinhart
@ JonathonRainhart: Das weiß ich. Aber ich benutze immer hex, wenn ich Bitfelder benutze. Ich bin mir nicht sicher, ob ich mich intstattdessen sicher fühlen würde . Ich weiß, dass es dumm ist ... aber Gewohnheiten sterben schwer.
LightStriker
36

Ich denke, es liegt nur daran, dass die Sequenz immer 1,2,4,8 ist und dann eine 0 hinzufügt.
Wie Sie sehen können:

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

und so weiter, solange Sie sich an die Sequenz 1-2-4-8 erinnern, können Sie alle nachfolgenden Flags erstellen, ohne sich an die Potenzen von 2 erinnern zu müssen

VRonin
quelle
13

Denn [Flags]bedeutet, dass die Aufzählung wirklich ein Bitfeld ist . Mit können [Flags]Sie die bitweisen Operatoren AND ( &) und OR ( |) verwenden, um die Flags zu kombinieren. Bei solchen Binärwerten ist es fast immer klarer, hexadezimale Werte zu verwenden. Dies ist genau der Grund, warum wir in erster Linie Hexadezimal verwenden . Jedes Hex-Zeichen entspricht genau einem Halbbyte (vier Bits). Mit Dezimalzahl gilt diese 1-zu-4-Zuordnung nicht.

Jonathon Reinhart
quelle
2
Die Fähigkeit, bitweise Operationen zu verwenden, hat eigentlich nichts mit dem Attribut flags zu tun.
Mattias Nordqvist
4

Weil es eine mechanische, einfache Möglichkeit gibt, eine Zweierpotenz in hex zu verdoppeln. In Dezimalzahlen ist dies schwierig. Es erfordert eine lange Vermehrung in Ihrem Kopf. In hex ist es eine einfache Änderung. Sie können dies bis zu einem Punkt ausführen, 1UL << 63den Sie nicht dezimal ausführen können.

usr
quelle
1
Ich denke, das macht am meisten Sinn. Für Aufzählungen mit einer großen Anzahl von Werten ist dies der einfachste Weg (mit der möglichen Ausnahme des Beispiels von @ Hypercube).
Adi Lester
3

Weil es für Menschen einfacher ist, zu folgen, wo sich die Bits in der Flagge befinden. Jede hexadezimale Ziffer kann in eine 4-Bit-Binärzahl passen.

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

... and so on

0xF = 1111

Normalerweise möchten Sie, dass Ihre Flags keine Bits überlappen. Die einfachste Möglichkeit, dies zu tun und zu visualisieren, besteht darin, Ihre Flags mit hexadezimalen Werten zu deklarieren.

Wenn Sie also Flags mit 16 Bit benötigen, verwenden Sie 4-stellige Hexadezimalwerte. Auf diese Weise können Sie fehlerhafte Werte vermeiden:

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
Nur du
quelle
Diese Erklärung ist gut ... es fehlt nur noch das binäre Äquivalent, um die endgültige Aufstellung von Bit, Halbbytes und Bytes
anzuzeigen