¿Debería un Enum comenzar con un 0 o un 1?

136

Imagine que he definido la siguiente enumeración:

public enum Status : byte
{
    Inactive = 1,
    Active = 2,
}

¿Cuál es la mejor práctica para usar enum? Debería comenzar con 1el ejemplo anterior o comenzar 0(sin los valores explícitos) como este:

public enum Status : byte
{
    Inactive,
    Active
}
Acaz Souza
fuente
13
¿Realmente necesitas numerarlos explícitamente?
Yuck
164
Las enumeraciones se crearon solo para que cosas como esta no fueran importantes.
BoltClock
9
@Daniel - ¡arrgh no! Es mejor usar una enumeración cuando crees que un booleano servirá que usar un booleano cuando estás pensando en una enumeración.
AAT
22
@Daniel debido al valor FileNotFound, por supuesto
Joubarc
55
xkcd.com/163 se aplica a enum incluso mejor que a los índices de matriz.
Leftaroundabout

Respuestas:

161

Pautas de diseño del marco :

✔️ DEBE proporcionar un valor de cero en enumeraciones simples.

Considere llamar al valor algo así como "Ninguno". Si dicho valor no es apropiado para esta enumeración en particular, al valor predeterminado más común para la enumeración se le debe asignar el valor subyacente de cero.

Pautas de diseño del marco / Diseño de enumeraciones de marca :

❌ EVITE usar valores de enumeración de indicador de cero a menos que el valor represente "todos los indicadores se borren" y se nombre de manera apropiada, según lo prescrito en la siguiente directriz.

✔️ NOMBRE el valor cero de las enumeraciones de bandera Ninguno. Para una enumeración de marca, el valor siempre debe significar "se borran todas las banderas".

Andrey Taptunov
fuente
28
Fallo temprano: si 'Ninguno' no es apropiado pero no hay un valor predeterminado lógico, todavía pondría un valor de cero (y lo llamaría 'Ninguno' o 'Inválido') que no debe usarse, solo para que Si un miembro de la clase de esa enumeración no se inicializa correctamente, el valor no inicializado se puede detectar fácilmente, y en las switchdeclaraciones saltará a la defaultsección, donde arrojo un InvalidEnumArgumentException. De lo contrario, un programa puede continuar ejecutándose involuntariamente con el valor cero de una enumeración, que puede ser válida y pasar desapercibida.
Allon Guralnek
1
@Allon Parece mejor dar solo los valores de enumeración que sabes que son válidos y luego buscar valores no válidos en el setter y / o el constructor. De esa manera, sabrá de inmediato si algún código no funciona correctamente en lugar de permitir que exista un objeto con datos no válidos durante un tiempo desconocido y descubrirlo más tarde. A menos que 'Ninguno' represente un estado válido, no debe usarlo.
wprl
@SoloBold: Eso suena como un caso en el que no te olvides de inicializar un miembro de clase enum en un constructor. Ninguna cantidad de validación lo ayudará si olvida inicializar u olvida validar. Además, hay clases simples de DTO que no tienen constructores, sino que se basan en inicializadores de objetos . Cazar un insecto así puede ser extremadamente doloroso. Sin embargo, agregar un valor de enumeración no utilizado crea una API fea. Lo evitaría para las API orientadas al consumo público.
Allon Guralnek
@Allon Ese es un buen punto, pero diría que deberías validar enumeraciones en la función de establecimiento y lanzar desde el getter si nunca se llamó al setter. De esa manera, tiene un punto de falla y puede planificar en el campo siempre teniendo un valor válido, lo que simplifica el diseño y el código. Mis dos centavos de todos modos.
wprl
@SoloBold: Imagine una clase DTO con 15 propiedades implementadas automáticamente. Su cuerpo es de 15 líneas de largo. Ahora imagine esa misma clase con propiedades regulares. Eso es un mínimo de 180 líneas antes de agregar cualquier lógica de verificación. Esta clase se usa exclusivamente internamente solo para fines de transferencia de datos. ¿Cuál preferirías mantener, una clase de 15 líneas o una clase de más de 180 líneas? La concisión tiene su valor. Pero aún así, nuestros dos estilos son correctos, simplemente son diferentes. (Supongo que aquí es donde AOP entra y gana ambos lados de la discusión).
Allon Guralnek
66

Bueno, supongo que estoy en desacuerdo con la mayoría de las respuestas que dicen no enumerarlas explícitamente. Siempre los enumero explícitamente, pero eso es porque en la mayoría de los casos termino persistiéndolos en un flujo de datos donde se almacenan como un valor entero. Si no agrega explícitamente los valores y luego agrega un nuevo valor, puede romper la serialización y luego no podrá cargar con precisión los objetos antiguos persistentes. Si va a hacer algún tipo de almacenamiento persistente de estos valores, le recomiendo que los configure explícitamente.

pstrjds
fuente
9
+1, de acuerdo, pero solo en el caso de que su código se base en el entero por alguna razón externa (por ejemplo, serialización). En cualquier otro lugar, debe seguir dejando que el marco haga su trabajo. Si confía en los valores enteros internamente, entonces probablemente esté haciendo algo mal (vea: deje que el marco haga su trabajo).
Matthew Scharley
3
Me gusta persistir en el texto de la enumeración. Hace que la base de datos sea mucho más usable.
Dave el
2
Normalmente, no necesita establecer explícitamente los valores ... incluso cuando se serializan. solo SIEMPRE agregue nuevos valores al final. Esto resolverá los problemas de serialización. de lo contrario, es posible que necesite una versión de su almacén de datos (por ejemplo, un encabezado de archivo que contenga una versión para cambiar el comportamiento al leer / escribir valores) (o vea el patrón de recuerdo)
Beachwalker
44
@Dave: a menos que usted documente explícitamente que los textos de enumeración son sagrados, se prepara para fallar si un futuro programador decide ajustar un nombre para que sea más claro o conforme a alguna convención de nomenclatura.
supercat
1
@pstrjds: supongo que es una compensación estilística; sin embargo, el espacio en disco es barato, el tiempo dedicado a la conversión constante entre valores de enumeración y una búsqueda de la base de datos es relativamente costoso (doble si tiene una configuración de herramienta de informes en una base de datos o algo similar) . Si está preocupado por el espacio, con las versiones más recientes del servidor SQL puede comprimir una base de datos, lo que significa que 1000 ocurrencias de "SomeEnumTextualValue" utilizarán apenas más espacio. Por supuesto, esto no funcionará para todos los proyectos, es una compensación. ¡Creo que la preocupación por el ancho de banda huele a optimización prematura, tal vez!
Dave
15

Un Enum es un tipo de valor y su valor predeterminado (por ejemplo, para un campo Enum en una clase) será 0 si no se inicializa explícitamente.

Por lo tanto, generalmente desea tener 0 como una constante definida (por ejemplo, Desconocida).

En su ejemplo, si desea Inactiveser el predeterminado, debería tener el valor cero. De lo contrario, es posible que desee considerar agregar una constante Unknown.

Algunas personas han recomendado que no especifique explícitamente valores para sus constantes. Probablemente sea un buen consejo en la mayoría de los casos, pero hay algunos casos en los que querrás hacerlo:

  • Banderas enums

  • Enums cuyos valores se utilizan en interoperabilidad con sistemas externos (por ejemplo, COM).

Joe
fuente
Descubrí que las enumeraciones de banderas son mucho más legibles cuando no establece explícitamente los valores. - También es menos propenso a errores para permitir que el compilador haga los cálculos binarios por usted. ( [Flags] enum MyFlags { None = 0, A, B, Both = A | B, /* etc. */ }es decir, es mucho más legible que [Flags] enum MyFlags { None = 0, A = 1, B = 2, Both = 3, /* etc */ })
BrainSlugs83
1
@ BrainSlugs83 - No veo cómo eso sería útil en el caso general, por ejemplo, [Flags] enum MyFlags { None=0, A, B, C } daría como resultado [Flags] enum MyFlags { None=0, A=1, B=2, C=3 }, mientras que para una enumeración de Banderas normalmente querría C = 4.
Joe
14

A menos que tenga un motivo específico para cambiarlo, deje las enumeraciones con sus valores predeterminados, que comienzan en cero.

public enum Status : byte
{
    Inactive,
    Active
}
PescadoCanasta
fuente
6

Yo diría que la mejor práctica es no numerarlos y dejar que sea implícito, lo que comenzaría desde 0. Dado que es implícito, es la preferencia de idioma que siempre es bueno seguir :)

John Humphreys - w00te
fuente
6

Comenzaría una enumeración de tipo booleano con un 0.

A menos que "Inative" signifique algo diferente a "Inactive" :)

Esto conserva el estándar para aquellos.

Mark Schultheiss
fuente
6

Yo diría que depende de cómo los uses. Para marcar enum es una buena práctica tener 0 para el Nonevalor, así:

[Flags]
enum MyEnum
{
    None = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    All = Option1 | Option2 | Option3,
}

Cuando es probable que su enumeración se asigne a una tabla de búsqueda de base de datos, comenzaría con 1. No debería importar mucho el código escrito profesionalmente, pero esto mejora la legibilidad.

En otros casos, lo dejaría como está, sin importar si comienzan con 0 o 1.

Michael Sagalovich
fuente
5

A menos que tenga una buena razón para usar los valores sin procesar, solo debe usar valores implícitos y hacer referencia a ellos con Status.Activey Status.Inactive.

El problema es que es posible que desee almacenar datos en un archivo plano o base de datos, o utilizar un archivo plano o base de datos que otra persona creó. Si lo está haciendo usted mismo, hágalo de manera que la numeración se ajuste para lo que se usa el Enum.

Si los datos no son suyos, por supuesto, querrá usar lo que el desarrollador original haya usado como esquema de numeración.

Si planea usar Enum como un conjunto de indicadores, hay una convención simple que vale la pena seguir:

enum Example
{
  None      = 0,            //  0
  Alpha     = 1 << 0,       //  1
  Beta      = 1 << 1,       //  2
  Gamma     = 1 << 2,       //  4
  Delta     = 1 << 3,       //  8
  Epsilon   = 1 << 4,       // 16
  All       = ~0,           // -1
  AlphaBeta = Alpha | Beta, //  3
}

Los valores deben ser potencias de dos y pueden expresarse mediante operaciones de desplazamiento de bits. None, obviamente debería ser 0, pero Alles menos obvio -1. ~0es la negación binaria de 0y da como resultado un número que tiene cada bit establecido en 1, que representa un valor de-1 . Para las banderas compuestas (a menudo utilizadas por conveniencia), se pueden fusionar otros valores usando el operador o el bit |.

zzzzBov
fuente
3

No asignes ningún número. Solo úsalo como se supone que debe usarse.

Bebida alcohólica
fuente
3

Si no se especifica, la numeración comienza en 0.

Es importante ser explícito ya que las enumeraciones a menudo se serializan y almacenan como int, no como una cadena.

Para cualquier enumeración almacenada en la base de datos, siempre enumeramos explícitamente las opciones para evitar cambios y reasignaciones durante el mantenimiento.

Según Microsoft, la convención recomendada es usar la primera opción cero para representar un valor predeterminado no inicializado o el más común.

A continuación se muestra un acceso directo para comenzar a numerar en 1 en lugar de 0.

public enum Status : byte
{
    Inactive = 1,
    Active
}

Si desea establecer valores de indicador para utilizar operadores de bits en valores de enumeración, no comience la numeración en el valor cero.

dru
fuente
2

Si comienza en 1, puede obtener fácilmente un recuento de sus cosas.

{
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

Si comienza en 0, utilice el primero como valor para cosas no inicializadas.

{
    BOX_NO_THING   = 0,
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};
Jonathan Cline IEEE
fuente
55
Lo siento Jonathan. Creo que esta sugerencia es una pequeña "vieja escuela" en mi mente (una especie de convención proviene de años de bajo nivel c). Esto está bien como una solución rápida para "incrustar" algunas informaciones adicionales sobre la enumeración, pero esta no es una buena práctica en sistemas más grandes. No debe usar una enumeración si necesita información sobre el número de valores disponibles, etc. ¿Y qué pasa con un BOX_NO_THING1? ¿Le darías BOX_NO_THING + 1? Las enumeraciones deben usarse para lo que están destinadas a ser utilizadas: valores específicos (int) representados por nombres "hablantes".
Beachwalker
Hm. Supongo que es de la vieja escuela porque utilicé todas las mayúsculas, supongo, en lugar de MicrosoftBumpyCaseWithLongNames. Aunque estoy de acuerdo, es mejor usar iteradores que bucles hasta alcanzar una definición XyzNumDefsInMyEnum enumerada.
Jonathan Cline IEEE
Esta es una práctica terrible en C # en una variedad de formas. Ahora, cuando vaya a contar sus enumeraciones de la manera correcta, o si intenta enumerarlas de la manera correcta, obtendrá un objeto adicional duplicado. También hace que la llamada .ToString () sea potencialmente ambigua (arruinando la serialización moderna), entre otras cosas.
BrainSlugs83
0

En primer lugar, a menos que esté especificando valores específicos por una razón (el valor numérico tiene significado en otro lugar, es decir, la base de datos o el servicio externo), no especifique valores numéricos en absoluto y deje que sean explícitos.

En segundo lugar, siempre debe tener un elemento de valor cero (en enumeraciones sin marcas). Ese elemento se usará como el valor predeterminado.

Justin Niessner
fuente
0

No los comience en 0 a menos que haya una razón para hacerlo, como usarlos como índices para una matriz o lista, o si hay alguna otra razón práctica (como usarlos en operaciones bit a bit).

Su enumdebe comenzar exactamente donde se necesita. No tiene por qué ser secuencial tampoco. Los valores, si se establecen explícitamente, deben reflejar algún significado semántico o consideración práctica. Por ejemplo, una enumde las "botellas en la pared" debe numerarse del 1 al 99, mientras que una enumpotencia de 4 probablemente debe comenzar en 4 y continuar con 16, 64, 256, etc.

Además, solo se debe agregar un elemento de valor cero enumsi representa un estado válido. A veces, "ninguno", "desconocido", "perdido", etc., son valores válidos, pero muchas veces no lo son.

wprl
fuente
-1

Me gusta comenzar mis enumeraciones en 0, ya que ese es el valor predeterminado, pero también me gusta incluir un valor Desconocido, con un valor de -1. Esto se convierte en el valor predeterminado y puede ayudar con la depuración a veces.

Tomás McGuinness
fuente
44
Idea horrible. Como tipo de valor, las enumeraciones siempre se inicializan a cero. Si va a tener algún valor que represente lo desconocido o no inicializado, debe ser 0. No puede cambiar el valor predeterminado a -1, el relleno cero está codificado en todo el CLR.
Ben Voigt
Ah, no me di cuenta de eso. Generalmente establezco el valor de un valor / propiedad enum cuando calculo / inicializo. Gracias por la anotación.
Tomas McGuinness