Perché le enumerazioni dei flag sono generalmente definite con valori esadecimali


122

Molte volte vedo dichiarazioni di flag enum che utilizzano valori esadecimali. Per esempio:

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

Quando dichiaro un enum, di solito lo dichiaro in questo modo:

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

C'è una ragione o una logica per cui alcune persone scelgono di scrivere il valore in esadecimale anziché in decimale? Per come la vedo io, è più facile confondersi quando si usano valori esadecimali e scrivere accidentalmente Flag5 = 0x16invece di Flag5 = 0x10.


4
Cosa renderebbe meno probabile che tu scriva 10piuttosto che 0x10se usassi numeri decimali? Soprattutto dal momento che si tratta di numeri binari con cui abbiamo a che fare e l'esadecimale è banalmente convertibile in / da binario? 0x111è molto meno fastidioso tradurre nella propria testa che 273...
cHao

4
È un peccato che C # non abbia una sintassi che non richiede esplicitamente la scrittura dei poteri di due.
Colonel Panic

Stai facendo qualcosa di senza senso qui. L'intento dietro le bandiere è che saranno combinate bit per bit. Ma le combinazioni bit per bit non sono elementi del tipo. Il valore Flag1 | Flag2è 3 e 3 non corrisponde ad alcun valore di dominio di MyEnum.
Kaz

Dove lo vedi? con riflettore?
giammin

@giammin È una domanda generale, non su un'implementazione specifica. Ad esempio, puoi prendere progetti open source o solo codice disponibile in rete.
Adi Lester

Risposte:


184

I razionali possono differire, ma un vantaggio che vedo è che l'esadecimale ti ricorda: "Ok, non abbiamo più a che fare con i numeri nell'arbitrario mondo inventato dall'uomo di base dieci. Abbiamo a che fare con i bit - il mondo della macchina - e noi giocheremo secondo le sue regole. " L'esadecimale viene utilizzato raramente a meno che non si tratti di argomenti di livello relativamente basso in cui il layout di memoria dei dati è importante. Usarlo suggerisce che questa è la situazione in cui ci troviamo ora.

Inoltre, non sono sicuro di C #, ma so che in C x << yè una costante del tempo di compilazione valida. L'utilizzo di turni di bit sembra il più chiaro:

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

7
È molto interessante ed è anche un'enumerazione C # valida.
Adi Lester

13
+1 con questa notazione non commetti mai errori nei calcoli del valore enum
Sergey Berezovskiy

1
@AllonGuralnek: il compilatore assegna posizioni di bit univoche data l'annotazione [Flags]? Generalmente inizia da 0 e va in incrementi di 1, quindi qualsiasi valore di enum assegnato a 3 (decimale) sarebbe 11 in binario, impostando due bit.
Eric J.

2
@ Eric: Huh, non so perché ma sono sempre stato certo che assegnasse valori di potenze di due. Ho appena controllato e credo di essermi sbagliato.
Allon Guralnek

38
Un altro fatto divertente con la x << ynotazione. 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GBE così via. È davvero bello se vuoi creare un array da 16 KB per un buffer puoi semplicemente andarevar buffer = new byte[16 << 10];
Scott Chamberlain

43

Rende facile vedere che si tratta di flag binari .

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

Anche se la progressione lo rende ancora più chiaro:

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

3
In realtà aggiungo uno 0 davanti a 0x1, 0x2, 0x4, 0x8 ... Quindi ottengo 0x01, 0x02, 0x04, 0x08 e 0x10 ... Lo trovo più facile da leggere. Sto incasinando qualcosa?
LightStriker

4
@Light - Niente affatto. È molto comune, quindi puoi vedere come si allineano. Rende solo le parti più esplicite :)
Oded

2
@LightStriker solo andando a buttare là fuori che non importa se non si utilizza esadecimale. I valori che iniziano solo con uno zero vengono interpretati come ottali. Così 012è in realtà 10.
Jonathon Reinhart

@ JonathonRainhart: questo lo so. Ma uso sempre esadecimale quando uso i campi di bit. Non sono sicuro che mi sentirei al sicuro usando intinvece. So che è stupido ... ma le abitudini sono dure a morire.
LightStriker

36

Penso che sia solo perché la sequenza è sempre 1,2,4,8 e quindi aggiungi uno 0.
Come puoi vedere:

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

e così via, fintanto che ricordi la sequenza 1-2-4-8 puoi costruire tutti i flag successivi senza dover ricordare le potenze di 2


13

Perché [Flags]significa che l'enumerazione è davvero un campo di bit . Con [Flags]puoi usare gli operatori AND ( &) e OR ( |) bit per bit per combinare i flag. Quando si ha a che fare con valori binari come questo, è quasi sempre più chiaro utilizzare valori esadecimali. Questo è il vero motivo per cui usiamo esadecimale in primo luogo. Ogni carattere esadecimale corrisponde esattamente a un bocconcino (quattro bit). Con i decimali, questa mappatura da 1 a 4 non è vera.


2
La possibilità di utilizzare operazioni bit per bit non ha nulla a che fare con l'attributo flags, in realtà.
Mattias Nordqvist

4

Perché c'è un modo semplice e meccanico per raddoppiare una potenza di due in esagono. In decimale, questo è difficile. Richiede una lunga moltiplicazione nella tua testa. In hex è un semplice cambiamento. Puoi eseguire tutto questo fino a 1UL << 63che non puoi farlo in decimale.


1
Penso che questo abbia più senso. Per le enumerazioni con un ampio insieme di valori è il modo più semplice (con la possibile eccezione dell'esempio di @ Hypercube).
Adi Lester

3

Perché è più facile seguire per gli esseri umani dove i bit sono nella bandiera. Ogni cifra esadecimale può contenere un binario a 4 bit.

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

... and so on

0xF = 1111

Tipicamente vuoi che i tuoi flag non si sovrappongano ai bit, il modo più semplice per farlo e visualizzarli è usare valori esadecimali per dichiarare i tuoi flag.

Quindi, se hai bisogno di flag con 16 bit, utilizzerai valori esadecimali a 4 cifre e in questo modo puoi evitare valori errati:

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

Questa spiegazione è buona .... l'unica cosa che manca è l'equivalente binario per mostrare la linea finale di bit, bocconcini e byte;)
GoldBishop
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.