2008-12-06 10 views
8

Estábamos teniendo un debate si las enumeraciones deberían tener valores no inicializados. Por ejemplo. TenemosDeberían los enums tener valores no inicializados.

public enum TimeOfDayType 
{ 
    Morning 
    Afternoon 
    Evening 
} 

o

public enum TimeOfDayType 
{ 
    None 
    Morning 
    Afternoon 
    Evening 
} 

creo que no debería haber ninguna ninguno pero luego se va por defecto a un valor válido en la inicialización. Pero otros pensaron que debería haber alguna indicación de estado unitizado al tener otra enumeración que sea None o NotSet.

thoughts?

Respuesta

13

Hablando de tipos que aceptan nulos - Creo que se pueden usar para resolver el problema de forzar/no forzar la inicialización de una enumeración. Digamos que tenemos

enum Color { Red, Blue } 

Y digamos que usted tiene una función:

void Draw(Color c); 

Esa función dice que requiere un válida Color. Sin embargo, también podríamos tener esta función:

void Draw(Color? c); 

que dice que el programa puede manejar no se pasa de un color (null sería trasladado para indicar "no importa").

Bueno, es una alternativa a los miembros None.

+0

De acuerdo, la solución más limpia es un tipo que admite nulo. También se asigna a los esquemas DB. –

+9

Es lamentable que las enumeraciones en .NET no lo obliguen a pasar un valor válido en absoluto. Podría pasar (Color) 123123 que se compilaría y funcionaría bien - el método Draw debería hacer la validación :( –

+2

Probablemente una herencia de C, donde el compilador no sabe si se usa una enumeración como un verdadero enumerado tipo, o como un conjunto de banderas. Así que arroja sus manos al aire y se rinde. –

1

Depende de cómo se usa el tipo. A menudo, es más fácil para los usuarios del tipo no tener un valor "indefinido", porque no es necesario tener un valor de caso especial. Pero si necesita uno (porque los valores a veces necesitan estar en un estado que de otra manera no es ninguno de los valores enumerados), entonces necesita uno. Por lo general, no guarda ningún código de caso especial al usar dos enums en lugar de uno.

Es un poco como preguntar si debe usar tipos que aceptan nulos.

3

Simplemente agregando a la respuesta de Franks, una de las únicas veces que optaría por un elemento 'Ninguno' en una enumeración sobre anulable es cuando la enumeración se usa como banderas. El elemento "Ninguno" sería id de 0.

4

Se ha propuesto una solución de nulos como solución en algunas de las respuestas anteriores. Pero una enumeración anulable tiene la desventaja de que hace que los clientes verifiquen un valor nulo cada vez que usan la enumeración. Por el contrario, si tiene un valor predeterminado de "Ninguno", tiene la opción de usar un interruptor para los valores significativos y simplemente ignorar "Ninguno", sin tener que preocuparse de que la variable enum sea nula.

De todos modos, creo que tener un valor predeterminado de "Ninguno" o hacer que la enumeración sea nulable tiene sentido solo si la enumeración se usa como argumento en un constructor predeterminado para alguna clase. Y tiene que preguntarse a sí mismo: ¿no deberían los objetos de esa clase tener algún valor predeterminado significativo? Usando su ejemplo con la enumeración TimeOfDayType - si inicializa un objeto con TimeOfDayType.None, no puede usarlo de todos modos antes de cambiar el valor a Morning, Afternoon o Evening. Entonces, ¿no podría decir que el valor predeterminado es Mañana en lugar de Ninguno? O, lo que es aún mejor, ¿no podrías crear tus objetos una vez que ya sabes qué valor enum necesitan? Creo que si el problema se aborda correctamente en las primeras etapas de diseño, no debería necesitar un valor predeterminado especial para sus enums.

Por supuesto, todo lo anterior es una generalización.Tal vez no se puede aplicar a su escenario particular, por lo que si proporciona algunos detalles al respecto, podríamos analizar el tema más a fondo.

+0

Ese es un buen análisis de la causa raíz. Siempre dé un paso atrás primero. –

4

En el abscence de un miembro de "default", creo que es valioso tener un valor que representa el literal int 0.

No importa qué, una enumeración dada será creado con el valor literal 0. La mayor parte caso directo aquí es como un miembro de una estructura. Una estructura C# siempre tendrá un constructor predeterminado vacío que inicializa todos los campos a su valor predeterminado. En el caso de una enumeración, ese será el valor literal 0. La pregunta es cómo manejarlo.

Para mí, esto es un problema de estilo: si la enumeración no se inicializa explícitamente a un valor, ¿se le debe dar un valor válido arbitrario o un valor específico que indique una falta de inicialización explícita?

enum Color { Unknown, Red, Blue } 
enum Color2 { Red,Blue } 
struct Example<T> { 
    Color color; 
} 

static void SomeMethod() { 
    var v1 = new Example<Color>(); 
    var v2 = new Example<Color2>(); 
} 

En el caso de v1, si se inspecciona el campo de color Será explícitamente ser etiquetado como un campo no inicializado. En v2, el campo simple será "Rojo". No hay forma de que un programador detecte entre y el conjunto explícito a "Rojo" o un valor predeterminado implícito a "Rojo".

Otro caso donde esto causa un problema es hacer una instrucción de conmutación contra un valor enum. Vamos a alterar levemente la definición de Color2.

enum Color2 { Red = 1, Blue = 2 } 
static void SomeOtherMethod(p1 as Example<Color2>) { 
    switch (p1.color) { 
    case Color.Red: {} 
    case Color.Blue: {} 
    default: {throw new Exception("What happened?"); } 
    } 
} 

El modificador maneja todos los valores explícitos de la enumeración. Sin embargo, este código fallará para el constructor predeterminado del ejemplo <Color2> y no hay manera de suprimir este constructor.

Esto nos lleva a una regla ligeramente más importante: tener un valor de enumeración explícita para el valor literal 0.

+1

No estoy seguro de por qué necesita tener un explícito valor enum para 0. Simplemente déjelo fuera e inicialice explícitamente los otros valores, por lo que Rojo = 1, Azul = 2 aquí, por ejemplo. Mismo efecto en su mayor parte, pero luego Enum.GetValues ​​etc. no incluirá el "desconocido "value. –

+0

Agrego un valor explícito porque establece las expectativas de manera apropiada. Puede haber y habrá 0 instancias valoradas de esa enumeración. Es una consecuencia ineludible de usarlo como miembro de tipo de valor. Siento que hace que el código más claro para dea explícitamente l con el Desconocido por adelantado. – JaredPar

+0

0 valores se mostrarán como 0 sin un valor explícito, y significa que no es necesario eliminarlo explícitamente cuando se mira la lista de valores "reales". Aunque supongo que depende del uso exacto. –

7

siempre me puse uno de mis literales de enumeración a cero. Este literal no siempre debe llamarse "Ninguno" o "No establecer". Depende si hay un literal que actúa muy bien como predeterminado.

Configuré uno a cero porque, las enumeraciones (excepto las enumeraciones que aceptan nulos) siempre se inicializan en CLR en memoria a cero. Y si no define uno de los literales, esta memoria contiene valores ilegales. También cuando usas enumeraciones como banderas. El valor predeterminado no se puede usar para hacer comparaciones bit a bit. El resultado siempre será cero.

Cuando habilita FxCop, comprueba si ha definido un literal como predeterminado. Parece ser una "buena práctica" cuando tienen una regla para eso.

Cuestiones relacionadas