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

14

Si estoy usando una instrucción switch 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 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 unitarias). 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é dices, comunidad?

Dakota del Sur
fuente
Digamos que MyEnum es un tipo no anulable.
SD
3
"Hoy" no es anulable. ¿Qué pasa mañana cuando ya no mantengas el código? ¿O qué pasa cuando se agrega "MyBiz" a la enumeración pero no el caso? Los comentarios de Caleb sobre el mantenimiento son muy relevantes.
1
Enséñele a su compilador que es un error fatal si hay un interruptor que no cubre todos los casos.
¿Qué pasa si alguien arroja un valor no válido para MyEnumluego pasarlo a través de su interruptor?
Mawg dice que reinstalar a Monica
1
¿Que lenguaje? Si es Java, debe poner un método dentro de Enum y simplemente llamarlo (polimorfismo), eliminando por switchcompleto la declaración.
user949300

Respuestas:

34

La inclusión del caso predeterminado no cambia la forma en que funciona su código, pero hace que su 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), está incluyendo una gran flecha roja para el interno que su empresa contrata el próximo verano para agregar un par de características. La flecha dice: "¡Hola, sí! ¡Estoy hablando con USTED! Si va a agregar otro valor a la enumeración, será mejor que agregue un caso aquí también". Ese esfuerzo adicional podría agregar algunos bytes al programa compilado, que 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: el compilador también puede detectar la situación descrita anteriormente, es decir, proteger contra los valores agregados a una enumeración en algún momento posterior. Clang (y gcc, creo) emitirá una advertencia de forma predeterminada si activa un tipo enumerado pero no tiene un caso que cubra todos los valores posibles en la enumeración. Entonces, por ejemplo, si elimina el defaultcaso de su conmutador y agrega un nuevo valor MyBaza la enumeración, recibirá una advertencia que dice:

Enumeration value 'MyBaz' not handled in switch

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

Caleb
fuente
2
Ok, me has convencido :) Solo tendré que aceptar la mella en los números de cobertura de mi código.
SD
@st No hay razón para que no pueda probar ese código. Simplemente haga una compilación de prueba que compile condicionalmente un valor adicional en su enumeración, y luego escriba una prueba unitaria que la use. Quizás eso no sea lo ideal, pero probablemente no sea el único caso en el que necesite probar el código que normalmente nunca se alcanzará.
Caleb
O bien, emite un valor que no es enum a su tipo de enumeración y lo usa.
Mawg dice que reinstalar a Monica
5

También estuve hablando con un compañero de trabajo sobre esto esta mañana; es realmente desafortunado, pero creo que manejar el valor predeterminado es necesario por seguridad, por dos razones:

Primero, como menciona su compañero de trabajo, protege el código del futuro 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 conmutada. 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 lo simple case default:y lanzar una excepción, se ha protegido contra este caso extraño en el que "nada" sucede pero "algo" debería haber sucedido.

Desafortunadamente, básicamente cada vez que escribo una declaración de cambio, es porque estoy revisando los casos de una enumeración. Realmente desearía que el lenguaje "aplicara el comportamiento de" lanzar por defecto "(al menos agregando una palabra clave).

Chris Phillips
fuente
3

Agregar un caso predeterminado, incluso si nunca espera alcanzarlo, puede ser algo bueno. Hará la depuración mucho más fácil si su código arroja una excepción "Esto no debería haber sucedido" de inmediato en lugar de más adelante en el programa, arrojando alguna excepción misteriosa o devolviendo resultados inesperados sin error.

Ryathal
fuente
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 NULLvalores y prevenirlos NullPointerExceptions.

FrustratedWithFormsDesigner
fuente
-1

Si tuviera alguna idea de cómo se puede ahorrar tiempo de máquina insistiendo en el caso predeterminado, no necesitaría hacer la pregunta. Silenciosamente no hacer nada en una condición de error no es aceptable: ¿detecta silenciosamente excepciones que nunca deberían suceder? Dejar "minas" para los programadores que te siguen, igualmente inaceptable.

Si su código nunca se va a cambiar o modificar, 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 cada idioma. Nuestro estándar de codificación Ada establece que con los conmutadores en las enumeraciones, el manejo explícito de todos los valores, sin valores predeterminados, es la forma preferida de manejar esta situación.

Mattnz
fuente
¿Por qué todos los -1?
mattnz
2
No te -1, pero creo que sería por tu actitud;)
Friek