Pourquoi les énumérations d'indicateurs sont généralement définies avec des valeurs hexadécimales

122

Souvent, je vois des déclarations d'énumération de drapeau qui utilisent des valeurs hexadécimales. Par exemple:

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

Lorsque je déclare une énumération, je la déclare généralement comme ceci:

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

Y a-t-il une raison ou une justification pour laquelle certaines personnes choisissent d'écrire la valeur en hexadécimal plutôt qu'en décimal? À mon avis, il est plus facile de se tromper lors de l'utilisation de valeurs hexadécimales et d'écrire accidentellement à la Flag5 = 0x16place de Flag5 = 0x10.

Adi Lester
la source
4
Qu'est-ce qui rendrait moins probable que vous écriviez 10plutôt que 0x10si vous utilisiez des nombres décimaux? D'autant que ce sont des nombres binaires avec lesquels nous avons affaire, et hex est trivialement convertible vers / depuis le binaire? 0x111est beaucoup moins ennuyeux à traduire dans sa tête que 273...
cHao
4
C'est dommage que C # n'ait pas de syntaxe qui n'exige pas explicitement d'écrire les puissances de deux.
Colonel Panic
Vous faites quelque chose de absurde ici. L'intention derrière les drapeaux est qu'ils seront combinés au niveau du bit. Mais les combinaisons binaires ne sont pas des éléments du type. La valeur Flag1 | Flag2est 3 et 3 ne correspond à aucune valeur de domaine de MyEnum.
Kaz du
Où voyez-vous cela? avec réflecteur?
giammin
@giammin C'est une question générale, pas une implémentation spécifique. Vous pouvez prendre des projets open source ou simplement du code disponible sur le net par exemple.
Adi Lester

Réponses:

184

Les raisonnements peuvent différer, mais un avantage que je vois est que l'hexadécimal vous rappelle: "D'accord, nous n'avons plus affaire à des nombres dans le monde arbitraire inventé par l'homme de la base dix. Nous avons affaire à des bits - le monde de la machine - et nous va jouer selon ses règles. " L'hexadécimal est rarement utilisé, sauf si vous avez affaire à des sujets de niveau relativement bas où la disposition de la mémoire des données est importante. Son utilisation laisse entendre que c'est la situation dans laquelle nous nous trouvons actuellement.

De plus, je ne suis pas sûr de C #, mais je sais qu'en C x << yest une constante de compilation valide. L'utilisation des décalages de bits semble la plus claire:

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1 << 0,
    Flag2 = 1 << 1,
    Flag3 = 1 << 2,
    Flag4 = 1 << 3,
    Flag5 = 1 << 4
}
existe pour tous
la source
7
C'est très intéressant et c'est aussi une énumération C # valide.
Adi Lester
13
+1 avec cette notation, vous ne faites jamais d'erreur dans les calculs de valeur d'énumération
Sergey Berezovskiy
1
@AllonGuralnek: Le compilateur attribue-t-il des positions de bits uniques compte tenu de l'annotation [Flags]? Généralement, il commence à 0 et va par incréments de 1, donc toute valeur d'énumération attribuée à 3 (décimal) serait 11 en binaire, définissant deux bits.
Eric J.
2
@Eric: Huh, je ne sais pas pourquoi mais j'étais toujours certain que cela attribuait des valeurs de puissances de deux. Je viens de vérifier, et je suppose que je me suis trompé.
Allon Guralnek
38
Un autre fait amusant avec la x << ynotation. 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GBEt ainsi de suite. C'est vraiment bien si vous voulez créer un tableau de 16 Ko pour un tampon, vous pouvez simplement y allervar buffer = new byte[16 << 10];
Scott Chamberlain
43

Cela permet de voir facilement que ce sont des indicateurs binaires .

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

Bien que la progression le rend encore plus clair:

Flag6 = 0x20  // == 00100000
Flag7 = 0x40  // == 01000000
Flag8 = 0x80  // == 10000000
Oded
la source
3
J'ajoute en fait un 0 devant 0x1, 0x2, 0x4, 0x8 ... Donc j'obtiens 0x01, 0x02, 0x04, 0x08 et 0x10 ... Je trouve cela plus facile à lire. Suis-je en train de gâcher quelque chose?
LightStriker
4
@Light - Pas du tout. C'est très courant afin que vous puissiez voir comment ceux-ci s'alignent. Rend juste les bits plus explicites :)
Oded
2
@LightStriker aller juste jeter là - bas qui ne importe si vous ne l' utilisez hex. Les valeurs qui commencent par un zéro seulement sont interprétées comme octales. Ainsi 012est en fait 10.
Jonathon Reinhart
@JonathonRainhart: Je le sais. Mais j'utilise toujours hex lorsque j'utilise des champs de bits. Je ne suis pas sûr que je me sentirais en sécurité à la intplace. Je sais que c'est stupide ... mais les habitudes meurent dur.
LightStriker
36

Je pense que c'est juste parce que la séquence est toujours 1, 2, 4, 8 et ensuite ajouter un 0.
Comme vous pouvez le voir:

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

et ainsi de suite, tant que vous vous souvenez de la séquence 1-2-4-8, vous pouvez construire tous les drapeaux suivants sans avoir à vous souvenir des pouvoirs de 2

VRonin
la source
13

Parce [Flags]que cela signifie que l'énumération est vraiment un bitfield . Avec, [Flags]vous pouvez utiliser les opérateurs binaires AND ( &) et OR ( |) pour combiner les indicateurs. Lorsqu'il s'agit de valeurs binaires comme celle-ci, il est presque toujours plus clair d'utiliser des valeurs hexadécimales. C'est la raison même pour laquelle nous utilisons l' hexadécimal en premier lieu. Chaque caractère hexadécimal correspond à exactement un quartet (quatre bits). Avec décimal, ce mappage 1 à 4 n'est pas vrai.

Jonathon Reinhart
la source
2
La possibilité d'utiliser des opérations au niveau du bit n'a en fait rien à voir avec l'attribut flags.
Mattias Nordqvist
4

Parce qu'il existe un moyen mécanique et simple de doubler une puissance de deux dans l'hexagone. En décimal, c'est difficile. Cela nécessite une longue multiplication dans votre tête. En hexadécimal, c'est un simple changement. Vous pouvez effectuer cela jusqu'au bout, 1UL << 63ce que vous ne pouvez pas faire en décimal.

usr
la source
1
Je pense que cela a le plus de sens. Pour les énumérations avec un grand ensemble de valeurs, c'est le moyen le plus simple (à l'exception possible de l'exemple de @ Hypercube).
Adi Lester
3

Parce qu'il est plus facile à suivre pour les humains où les bits sont dans le drapeau. Chaque chiffre hexadécimal peut contenir un binaire de 4 bits.

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

... and so on

0xF = 1111

En général, vous voulez que vos indicateurs ne chevauchent pas les bits, le moyen le plus simple de le faire et de le visualiser consiste à utiliser des valeurs hexadécimales pour déclarer vos indicateurs.

Donc, si vous avez besoin de drapeaux de 16 bits, vous utiliserez des valeurs hexadécimales à 4 chiffres et vous éviterez ainsi les valeurs erronées:

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
Seulement toi
la source
Cette explication est bonne ... la seule chose qui manque est l'équivalent binaire pour montrer la ligne ultime du bit, des grignotages et des octets;)
GoldBishop