instrucción de cambio: manejo del caso predeterminado cuando no se puede alcanzar

12

Si estoy usando una instrucción de cambio para manejar valores de una enumeración (que es propiedad de mi clase) y tengo un caso para cada valor posible, ¿vale la pena agregar un código para manejar el caso "predeterminado"?

enum MyEnum
{
    MyFoo,
    MyBar,
    MyBat
}

MyEnum myEnum = GetMyEnum();
switch (myEnum)
{
    case MyFoo:
        DoFoo();
        break;
    case MyBar:
        DoBar();
        break;
    case MyBat:
        DoBat();
        break;
    default:
        Log("Unexpected value");
        throw new ArgumentException() 
}

No creo que sea porque este código nunca se puede alcanzar (incluso con pruebas de unidad). Mi compañero de trabajo no está de acuerdo y cree que esto nos protege contra comportamientos inesperados causados por la adición de nuevos valores a MyEnum.

¿Qué te dice, comunidad?

    
pregunta s d 03.05.2012 - 19:35

5 respuestas

32

Incluir el caso predeterminado no cambia la forma en que funciona tu código, pero sí hace que tu código sea más fácil de mantener. Al hacer que el código se rompa de una manera obvia (registrar un mensaje y lanzar una excepción), se incluye una gran flecha roja para el interno que su empresa contrate el próximo verano para agregar un par de funciones. La flecha dice: "¡Oye, tú! ¡Sí, estoy hablando contigo! Si vas a agregar otro valor a la enumeración, es mejor que también agregues un caso aquí". Ese esfuerzo adicional podría agregar algunos bytes al programa compilado, lo cual es algo a considerar. Pero también salvará a alguien (tal vez incluso el futuro) en algún lugar entre una hora y un día de rascado improductivo.

Actualización: La situación descrita anteriormente, es decir, la protección contra los valores agregados a una enumeración en algún momento posterior, también puede ser detectada por el compilador. Clang (y gcc, creo) emitirá una advertencia de forma predeterminada si cambia un tipo enumerado pero no tiene un caso que cubra todos los valores posibles en la enumeración. Entonces, por ejemplo, si quita el caso default de su conmutador y agrega un nuevo valor MyBaz a la enumeración, recibirá una advertencia que dice:

Enumeration value 'MyBaz' not handled in switch

Dejar que el compilador detecte los casos no cubiertos es que elimina en gran medida la necesidad de ese caso default inalcanzable que inspiró su pregunta en primer lugar.

    
respondido por el Caleb 03.05.2012 - 19:55
5

También estuve hablando con un compañero de trabajo sobre esto esta mañana. Es realmente desafortunado, pero creo que es necesario manejar la seguridad por defecto, por dos motivos:

Primero, como lo menciona su compañero de trabajo, el futuro comprueba el código contra los nuevos valores que se agregan a la enumeración. Esto puede o no parecer una posibilidad, pero siempre está ahí.

Más importante aún, dependiendo del idioma / compilador, puede ser posible tener valores que no sean miembros de la enumeración en su variable cambiada. Por ejemplo, en C #:

MyEnum myEnum = (MyEnum) 3; // This could come from anywhere, maybe parsed from text?
// ... code goes on for a while

switch ( myEnum )
{
    case MyEnum.A:
        // ... handle A case
        break;
    case MyEnum.B:
        // ... handle B case
        break;
}

// ... code that expects either A or B to have happened

Al agregar el simple case default: y lanzar una excepción, se ha protegido contra este extraño caso en el que "no pasa nada", pero "algo" debería haber ocurrido.

Desafortunadamente, básicamente cada vez que escribo una instrucción de cambio, es porque estoy revisando los casos de una enumeración. Realmente deseo que el lenguaje "lanzar por defecto" pueda ser impuesto por el propio lenguaje (al menos agregando una palabra clave).

    
respondido por el Chris Phillips 04.05.2012 - 00:24
3

Agregar un caso predeterminado, incluso si nunca espera llegar a él, puede ser una buena cosa. Hará que la depuración sea mucho más fácil si su código lanza una excepción "Esto no debería haber ocurrido" de inmediato en lugar de más adelante en el programa, arrojando una excepción misteriosa o devolviendo resultados inesperados sin error.

    
respondido por el Ryathal 03.05.2012 - 19:48
2

Yo digo:

Intenta agregar otro tipo a MyEnum . Luego cambia esta línea:

MyEnum myEnum = GetMyEnum();

a

MyEnum myEnum = SomethingElse;

Luego ejecute su código con el caso predeterminado y sin el caso predeterminado. ¿Qué comportamiento prefieres?

Tener el caso predeterminado también puede ser útil para atrapar los valores de NULL y prevenir NullPointerExceptions .

    
respondido por el FrustratedWithFormsDesigner 03.05.2012 - 19:43
-1

Si tuviera alguna idea de cómo se puede ahorrar el tiempo de la máquina insistiendo en el caso predeterminado, no tendría que hacer la pregunta. No hacer nada en silencio en una condición de error no es aceptable. ¿Captura en silencio excepciones que nunca deberían ocurrir? Dejar "minas" para los programadores que lo siguen, igualmente inaceptable.

Si su código nunca va a ser cambiado o modificado, y está 100% libre de errores, omitir el caso predeterminado puede estar bien.

Ada (el abuelo de los lenguajes de programación robustos) no compilará un conmutador en una enumeración, a menos que todas las enumeraciones estén cubiertas o haya un controlador predeterminado: esta función está en mi lista de deseos para todos los idiomas. Nuestro estándar de codificación de Ada establece que con los conmutadores en las enumeraciones, la forma preferida de manejar esta situación es el manejo explícito de todos los valores, sin valor predeterminado.

    
respondido por el mattnz 04.05.2012 - 02:38

Lea otras preguntas en las etiquetas