Por que as enumerações de flag geralmente são definidas com valores hexadecimais

121

Muitas vezes, vejo declarações de flag enum que usam valores hexadecimais. Por exemplo:

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

Quando declaro uma enumeração, geralmente a declaro assim:

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

Existe uma razão ou razão para que algumas pessoas escolham escrever o valor em hexadecimal em vez de decimal? Na minha opinião, é mais fácil ficar confuso ao usar valores hexadecimais e escrever acidentalmente em Flag5 = 0x16vez de Flag5 = 0x10.

Adi Lester
fonte
4
O que tornaria menos provável que você escrevesse 10do que 0x10se usasse números decimais? Principalmente porque estamos lidando com números binários e hex é trivialmente conversível de / para binário? 0x111é muito menos irritante para traduzir em sua cabeça do que 273...
Chao
4
É uma pena que o C # não tenha uma sintaxe que não exija explicitamente a gravação dos poderes de dois.
Coronel Panic
Você está fazendo algo sem sentido aqui. A intenção por trás das bandeiras é que elas sejam combinadas bit a bit. Mas as combinações bit a bit não são elementos do tipo. O valor Flag1 | Flag2é 3 e 3 não corresponde a nenhum valor de domínio de MyEnum.
Kaz
Onde você vê isso? com refletor?
giammin
@giammin É uma pergunta geral, não sobre uma implementação específica. Você pode pegar projetos de código aberto ou apenas código disponível na rede, por exemplo.
Adi Lester

Respostas:

183

As razões podem diferir, mas uma vantagem que vejo é que o hexadecimal lembra: "Ok, não estamos mais lidando com números no mundo arbitrário e inventado pelo homem da base dez. Estamos lidando com bits - o mundo da máquina - e nós vai jogar de acordo com suas regras. " O hexadecimal raramente é usado, a menos que você esteja lidando com tópicos de nível relativamente baixo, onde o layout da memória dos dados é importante. Usá-lo sugere que essa é a situação em que estamos agora.

Além disso, não tenho certeza sobre C #, mas sei que em C x << yé uma constante válida em tempo de compilação. Usar deslocamento de bits parece o mais claro:

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1 << 0,
    Flag2 = 1 << 1,
    Flag3 = 1 << 2,
    Flag4 = 1 << 3,
    Flag5 = 1 << 4
}
existe-forall
fonte
7
Isso é muito interessante e também é realmente um enum C # válido.
Adi Lester #
13
+1 com esta notação você nunca fazem erro no cálculo do valor de enum
Sergey Berezovskiy
1
@AllonGuralnek: O compilador atribui posições de bits exclusivas, dada a anotação [Flags]? Geralmente começa em 0 e vai em incrementos de 1, portanto, qualquer valor de enum atribuído 3 (decimal) seria 11 em binário, configurando dois bits.
11747 Eric
2
@ Eric: Hein, não sei por que, mas sempre tive certeza de que atribuímos valores de potências de dois. Acabei de verificar e acho que estava errado.
Allon Guralnek 6/11/12
38
Outro fato divertido com a x << ynotação. 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GBE assim por diante. É muito bom se você quiser fazer uma matriz de 16 KB para um buffer que você pode simplesmente irvar buffer = new byte[16 << 10];
Scott Chamberlain
43

Torna fácil ver que esses são sinalizadores binários .

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

Embora a progressão torne ainda mais claro:

Flag6 = 0x20  // == 00100000
Flag7 = 0x40  // == 01000000
Flag8 = 0x80  // == 10000000
Oded
fonte
3
Na verdade, adiciono um 0 na frente de 0x1, 0x2, 0x4, 0x8 ... Então, recebo 0x01, 0x02, 0x04, 0x08 e 0x10 ... Acho isso mais fácil de ler. Estou estragando algo?
LightStriker
4
@ Light - Nem um pouco. É muito comum que você possa ver como eles se alinham. Só faz os bits mais explícito :)
Oded
2
@LightStriker indo só para jogá-lo lá fora, que faz importa se você não estiver usando hex. Valores que começam com apenas zero são interpretados como octais. Então 012é verdade 10.
Jonathon Reinhart
@ JonathonRainhart: Isso eu sei. Mas eu sempre uso hexadecimal quando uso campos de bits. Não tenho certeza se me sentiria seguro usando isso int. Eu sei que é estúpido ... mas os hábitos morrem muito.
LightStriker
36

Eu acho que é só porque a sequência é sempre 1,2,4,8 e depois adiciona um 0.
Como você pode ver:

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 assim por diante, desde que você se lembre da sequência 1-2-4-8, poderá criar todas as sinalizações subseqüentes sem precisar se lembrar dos poderes de 2

VRonin
fonte
13

Porque [Flags]significa que o enum é realmente um campo de bits . Com [Flags]você, você pode usar os operadores AND ( &) e OR ( |) bit a bit para combinar os sinalizadores. Ao lidar com valores binários como esse, é quase sempre mais claro usar valores hexadecimais. Esta é a razão pela qual usamos hexadecimal em primeiro lugar. Cada caractere hexadecimal corresponde a exatamente uma mordidela (quatro bits). Com decimal, esse mapeamento de 1 para 4 não é verdadeiro.

Jonathon Reinhart
fonte
2
A capacidade de usar operações bit a bit não tem nada a ver com o atributo flags, na verdade.
Mattias Nordqvist
4

Porque existe uma maneira mecânica e simples de dobrar uma potência de dois em hexadecimal. Em decimal, isso é difícil. Requer longa multiplicação em sua cabeça. Em hexadecimal, é uma mudança simples. Você pode fazer isso até o máximo 1UL << 63que não puder fazer em decimal.

usr
fonte
1
Eu acho que isso faz mais sentido. Para enumerações com um grande conjunto de valores, é o caminho mais fácil (com a possível exceção do exemplo do @ Hypercube).
Adi Lester
3

Porque é mais fácil seguir para humanos onde os bits estão na bandeira. Cada dígito hexadecimal pode caber em um binário de 4 bits.

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

... and so on

0xF = 1111

Normalmente, você deseja que seus sinalizadores não se sobreponham aos bits, a maneira mais fácil de visualizá-lo e usando valores hexadecimais para declarar seus sinalizadores.

Portanto, se você precisar de sinalizadores com 16 bits, usará valores hexadecimais de 4 dígitos e poderá evitar valores errados:

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
Só você
fonte
Esta explicação é bom .... única coisa que falta é o equivalente binário para mostrar a linha final up dos bits, mordidelas e bytes;)
GoldBishop