2009-02-27 15 views
15

Mi equipo ha comenzado recientemente a usar el documento Lance Hunt's C# Coding Standards como punto de partida para consolidar nuestros estándares de codificación.Estándares de codificación C# de Lance Hunt - confusión enum

Hay un elemento que simplemente no entiendo el punto, ¿alguien aquí puede arrojar algo de luz sobre él?

El artículo es número 77:

validar siempre una enumeración valor de la variable o parámetro antes consumirlo. Pueden contener cualquier valor que admita el tipo de Enum subyacente (valor predeterminado int).

Ejemplo:

public void Test(BookCategory cat) 
{ 
if (Enum.IsDefined(typeof(BookCategory), cat)) 
{…} 
} 

Respuesta

13

Creo que los comentarios de arriba respondieron la pregunta. Esencialmente, cuando escribí esta regla, intentaba transmitir una práctica de codificación defensiva para validar todas las entradas. Los mensajes son un caso especial porque muchos desarrolladores suponen incorrectamente que están validados, cuando no lo son. Como resultado, a menudo verá si las declaraciones o declaraciones de conmutación fallan para un valor enum indefinido.

Solo tenga en cuenta que, por defecto, una enumeración no es más que un contenedor alrededor de una INT, y la valida como si fuera una int.

Para una discusión más detallada del uso apropiado de enumeración, debes revisar los mensajes de blog por Brad Abrams y Krzysztof Cwalina o su excelente libro "Pautas de diseño de la estructura: Convenios, modismos y patrones para .NET reutilizables Bibliotecas"

17

enumeraciones no se comprueban:

enum Foo { A= 1, B = 2, C = 3} 
Foo x = (Foo) 27; // works fine 

Ahora usted tiene una Foo que no está definido. Él solo dice "revisa tu información". Tenga en cuenta que Enum.IsDefined es relativamente lento (basado en la reflexión). Personalmente, tiendo a usar un interruptor con una default que se produce una excepción:

switch(x) { 
    case Foo.A: /* do something */ break; 
    case Foo.B: /* do something */ break; 
    case Foo.C: /* do something */ break; 
    default: throw new ArgumentOutOfRangeException("x"); 
} 
+0

Estoy de acuerdo en que siempre debes tener un valor predeterminado: lanza una nueva ArgumentOutOfRangeException. También lo cubre para el escenario donde se agrega un nuevo valor a la enumeración. –

+2

Podría decirse que NotSupportedException podría ser más apropiado, pero diablos, ¡siempre y cuando arroje! –

+2

de acuerdo! Lanzar una nueva OldShoeException() es mejor que dejar que la lógica se caiga y un error aparezca en otro lado como consecuencia. –

1

Esto se debe a que es totalmente gratis para llamar a ese método de "prueba" de esta manera:

Test((BookCategory)-999); 

Eso castearás -999 como una categoría de libro, pero, por supuesto, el resultado (pasado como el parámetro "gato" para probar) no es un valor válido para la enumeración de categoría de libro.

+0

Creo que quieres decir "That'll cast -999" ... –

+0

Vaya, tienes razón, volví y edité una parte de la publicación, pero olvidé cambiar la otra parte. ¡Gracias! –

20

El punto es que puede esperar que al tener un parámetro de tipo BookCategory, siempre tenga una categoría de libro significativa . Ese no es el caso. Podría llamar a:

BookCategory weirdCategory = (BookCategory) 123456; 
Test(weirdCategory); 

Si una enumeración está destinado a representar un conjunto bien conocido de los valores, el código no se debe esperar para manejar con sensatez un valor fuera de ese conjunto bien conocido. La prueba verifica si el argumento es apropiado primero.

yo personalmente invertir la lógica sin embargo:

public void Test(BookCategory cat) 
{ 
    if (!Enum.IsDefined(typeof(BookCategory), cat)) 
    { 
     throw new ArgumentOutOfRangeException("cat"); 
    } 
} 

En C# 3 esto se puede hacer fácilmente con un método de extensión:

Uso:

public void Test(BookCategory cat) 
{ 
    cat.ThrowIfNotDefined("cat"); 
} 
+2

Es un dolor que los genéricos no permitan "Enum"/"enumeración" como una restricción ... –

+0

@Marc: De hecho. @Richard: Agregado. –

+0

Conector desvergonzado: mis clases de utilidad Trinity de Helper incluyen métodos de extensión para verificar argumentos, incluidas enumeraciones. http://thehelpertrinity.codeplex.com/Wiki/View.aspx?title=wiki%20documentation&referringTitle=Home#AssertEnumMember –

1

Si Si desea ampliar su enumeración con nuevos valores en futuras versiones de su biblioteca, no debe usar la construcción

si (Enum.IsDefined (typeof (BookCategory), cat))

ya que esto sólo comprueba si el tipo es válida de acuerdo con la definición de enumeración.Por otro lado, su código solo se probó para los valores permitidos actualmente y puede fallar para nuevos valores.

Consulte This weblog article para obtener más información.

+0

Bueno, si (más adelante) comienza a usar valores para sus enumeraciones que no están definidas en la enumeración, ¿de qué sirve tener una enumeración en primer lugar? –

+0

El punto es que luego puede definir nuevos valores enum válidos (recompilando su código) .. Pero código compilado contra su código no se probó con este nuevo valor ... y Enum.IsDefined simplemente devuelve true –

Cuestiones relacionadas