¿Por qué las enumeraciones de marca suelen definirse con valores hexadecimales

121

Muchas veces veo declaraciones de enumeración de bandera que usan valores hexadecimales. Por ejemplo:

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

Cuando declaro una enumeración, generalmente la declaro así:

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

¿Hay alguna razón o razón por la cual algunas personas eligen escribir el valor en hexadecimal en lugar de decimal? A mi modo de ver, es más fácil confundirse cuando se utilizan valores hexadecimales y escribir accidentalmente en Flag5 = 0x16lugar de Flag5 = 0x10.

Adi Lester
fuente
44
¿Qué haría que sea menos probable que escriba en 10lugar de 0x10usar números decimales? Particularmente ya que estos son números binarios con los que estamos tratando, y ¿hexadecimal es trivialmente convertible a / desde binario? 0x111es mucho menos molesto traducir en la cabeza que 273...
cHao
44
Es una pena que C # no tenga una sintaxis que no requiera explícitamente escribir los poderes de dos.
Coronel Panic el
Estás haciendo algo sin sentido aquí. La intención detrás de las banderas es que se combinarán bit a bit. Pero las combinaciones bit a bit no son elementos del tipo. El valor Flag1 | Flag2es 3 y 3 no corresponde a ningún valor de dominio de MyEnum.
Kaz el
¿Dónde viste eso? con reflector?
giammin
@giammin Es una pregunta general, no sobre una implementación específica. Puede tomar proyectos de código abierto o simplemente código disponible en la red, por ejemplo.
Adi Lester

Respuestas:

183

Los fundamentos pueden diferir, pero una ventaja que veo es que el hexadecimal te recuerda: "Está bien, ya no estamos tratando con números en el arbitrario mundo inventado por los humanos de la base diez. Estamos tratando con bits, el mundo de la máquina, y nosotros vamos a jugar según sus reglas ". El hexadecimal rara vez se usa a menos que se trate de temas de nivel relativamente bajo donde el diseño de memoria de los datos es importante. Usarlo sugiere el hecho de que esa es la situación en la que estamos ahora.

Además, no estoy seguro acerca de C #, pero sé que en C x << yhay una constante de tiempo de compilación válida. Usar cambios de bit parece lo más claro:

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1 << 0,
    Flag2 = 1 << 1,
    Flag3 = 1 << 2,
    Flag4 = 1 << 3,
    Flag5 = 1 << 4
}
existe para todos
fuente
77
Eso es muy interesante y en realidad también es una enumeración válida de C #.
Adi Lester el
13
+1 con esta notación nunca cometes un error en los cálculos de valores de enumeración
Sergey Berezovskiy
1
@AllonGuralnek: ¿El compilador asigna posiciones de bit únicas con la anotación [Flags]? Generalmente comienza en 0 y va en incrementos de 1, por lo que cualquier valor de enumeración asignado 3 (decimal) sería 11 en binario, configurando dos bits.
Eric J.
2
@Eric: Eh, no sé por qué, pero siempre tuve la certeza de que asignaba valores de potencia de dos. Acabo de comprobar, y supongo que estaba equivocado.
Allon Guralnek
38
Otro hecho divertido con la x << ynotación. 1 << 10 = KB`` 1 << 20 = MB, 1 << 30 = GBy así sucesivamente. Es realmente agradable si quieres hacer una matriz de 16 KB para un búfer, puedes irvar buffer = new byte[16 << 10];
Scott Chamberlain
43

Hace que sea fácil ver que estos son indicadores binarios .

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

Aunque la progresión lo hace aún más claro:

Flag6 = 0x20  // == 00100000
Flag7 = 0x40  // == 01000000
Flag8 = 0x80  // == 10000000
Oded
fuente
3
De hecho, agrego un 0 delante de 0x1, 0x2, 0x4, 0x8 ... Así que obtengo 0x01, 0x02, 0x04, 0x08 y 0x10 ... Me resulta más fácil de leer. ¿Estoy estropeando algo?
LightStriker
44
@ Light - No, en absoluto. Es muy común para que pueda ver cómo se alinean. Solo hace que los bits sean más explícitos :)
Oded
2
@LightStriker sólo va a tirar por ahí que hace importa si usted no está utilizando hexagonal. Los valores que comienzan con solo un cero se interpretan como octales. Así 012es en realidad 10.
Jonathon Reinhart el
@JonathonRainhart: Eso lo sé. Pero siempre uso hexadecimal cuando uso campos de bits. No estoy seguro de que me sentiría seguro usando en su intlugar. Sé que es estúpido ... pero los hábitos mueren mucho.
LightStriker
36

Creo que es solo porque la secuencia siempre es 1,2,4,8 y luego agrego un 0.
Como puede 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

y así sucesivamente, siempre que recuerde la secuencia 1-2-4-8, puede construir todas las banderas posteriores sin tener que recordar los poderes de 2

VRonin
fuente
13

Porque [Flags]significa que la enumeración es realmente un campo de bits . Con [Flags]usted puede usar los operadores bit a bit AND ( &) y OR ( |) para combinar las banderas. Cuando se trata con valores binarios como este, casi siempre es más claro usar valores hexadecimales. Esta es la razón por la que usamos hexadecimal en primer lugar. Cada carácter hexadecimal corresponde exactamente a un mordisco (cuatro bits). Con decimal, esta asignación de 1 a 4 no es verdadera.

Jonathon Reinhart
fuente
2
La capacidad de usar operaciones bit a bit no tiene nada que ver con el atributo flags, en realidad.
Mattias Nordqvist
4

Porque hay una forma mecánica y simple de duplicar una potencia de dos en hex. En decimal, esto es difícil. Requiere una larga multiplicación en tu cabeza. En hexadecimal es un cambio simple. Puede llevar a cabo esto hasta el punto 1UL << 63que no puede hacer en decimal.

usr
fuente
1
Creo que esto tiene más sentido. Para las enumeraciones con un gran conjunto de valores, es la forma más fácil de hacerlo (con la posible excepción del ejemplo de @ Hypercube).
Adi Lester
3

Porque es más fácil de seguir para los humanos donde están los bits en la bandera. Cada dígito hexadecimal puede caber en un binario de 4 bits.

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

... and so on

0xF = 1111

Por lo general, desea que sus banderas no se superpongan bits, la forma más fácil de hacerlo y visualizarlo es utilizando valores hexadecimales para declarar sus banderas.

Entonces, si necesita banderas con 16 bits, usará valores hexadecimales de 4 dígitos y de esa manera puede evitar valores erróneos:

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ólo tu
fuente
Esta explicación es buena ... lo único que falta es el equivalente binario para mostrar la alineación final de bits, mordiscos y bytes;)
GoldBishop