default - republica - ¿Las declaraciones de cambio siempre deben contener una cláusula predeterminada?
informacion de datos minimos requeridos para las operaciones de cambio por importaciones de bienes (19)
¿Las declaraciones de cambio siempre deben contener una cláusula predeterminada? No puede existir ningún caso de cambio sin el caso predeterminado, en este caso, el caso predeterminado activará el valor del switch(x)
en este caso x cuando no coincida con ningún otro valor de caso.
En una de mis primeras revisiones de código (hace un tiempo atrás), me dijeron que es una buena práctica incluir una cláusula predeterminada en todas las declaraciones de cambio. Recientemente recordé este consejo, pero no puedo recordar cuál fue la justificación. Suena bastante extraño para mí ahora.
¿Hay alguna razón razonable para incluir siempre una declaración por defecto?
¿Es este lenguaje dependiente? No recuerdo qué idioma estaba usando en ese momento, ¿tal vez esto se aplica a algunos idiomas y no a otros?
¿Una declaración de "cambio" debería incluir siempre una cláusula por defecto? No. Por lo general, debe incluir un valor predeterminado.
Incluir una cláusula predeterminada solo tiene sentido si hay algo que hacer, como afirmar una condición de error o proporcionar un comportamiento predeterminado. Incluir uno "solo porque" es la programación de carga de culto y no proporciona ningún valor. Es el "cambio" equivalente a decir que todas las declaraciones "if" deben incluir un "else".
Aquí hay un ejemplo trivial de donde no tiene sentido:
void PrintSign(int i)
{
switch (Math.Sign(i))
{
case 1:
Console.Write("positive ");
break;
case -1:
Console.Write("negative ");
break;
default: // useless
}
Console.Write("integer");
}
Este es el equivalente de:
void PrintSign(int i)
{
int sgn = Math.Sign(i);
if (sgn == 1)
Console.Write("positive ");
else if (sgn == -1)
Console.Write("negative ");
else // also useless
{
}
Console.Write("integer");
}
Al menos no es obligatorio en Java. Según JLS, dice que casi un caso predeterminado puede estar presente. Lo que significa que ningún caso por defecto es aceptable. En ocasiones, también depende del contexto en el que está utilizando la instrucción switch. Por ejemplo, en Java, el siguiente bloque de cambio no requiere un caso predeterminado
private static void switch1(String name) {
switch (name) {
case "Monday":
System.out.println("Monday");
break;
case "Tuesday":
System.out.println("Tuesday");
break;
}
}
Pero en el siguiente método que espera devolver un String, el caso predeterminado es útil para evitar errores de compilación
private static String switch2(String name) {
switch (name) {
case "Monday":
System.out.println("Monday");
return name;
case "Tuesday":
System.out.println("Tuesday");
return name;
default:
return name;
}
}
aunque puede evitar el error de compilación para el método anterior sin tener un caso predeterminado solo con una declaración de retorno al final, pero el hecho de que sea un caso predeterminado lo hace más legible.
Debe tener un valor predeterminado para capturar los valores no esperados que ingresan.
Sin embargo, no estoy de acuerdo con Adrian Smith en que su mensaje de error por defecto debería ser algo totalmente sin sentido. Puede haber un caso no manejado que no viste (que es el tipo de punto) que tu usuario terminará viendo y un mensaje como "inalcanzable" no tiene ningún sentido y no ayuda a nadie en esa situación.
Por ejemplo, ¿cuántas veces ha tenido un BSOD completamente sin sentido? ¿O una excepción fatal @ 0x352FBB3C32342?
Depende de cómo funciona el conmutador en un idioma particular, sin embargo, en la mayoría de los idiomas, cuando no se compara ningún caso, la ejecución cae a través de la declaración del conmutador sin previo aviso. Imagine que esperaba algún conjunto de valores y los manejó en el switch, sin embargo, obtiene otro valor en la entrada. No pasa nada y no sabes que no pasó nada. Si detectara el caso por defecto, sabría que había algo mal.
Diría que depende del idioma, pero en C si está activando un tipo de enumeración y maneja todos los valores posibles, probablemente sea mejor que NO incluya un caso predeterminado. De esa manera, si agrega una etiqueta de enumeración adicional más adelante y se olvida de agregarla al conmutador, un compilador competente le dará una advertencia sobre el caso que falta.
En mi empresa, escribimos software para el mercado de Aviónica y Defensa, y siempre incluimos una declaración por defecto, porque TODOS los casos en una declaración de cambio deben manejarse explícitamente (incluso si es solo un comentario que dice "No hacer nada"). No podemos permitirnos que el software se comporte mal o simplemente se bloquee con valores inesperados (o incluso con lo que creemos imposible).
Puede discutirse que un caso predeterminado no siempre es necesario, pero al exigirlo siempre, nuestros analizadores de código lo controlan fácilmente.
Es posible que el caso predeterminado no sea necesario en el conmutador utilizado por enum. cuando el interruptor contenía todo el valor, el caso predeterminado nunca se ejecutará. Así que en este caso, no es necesario.
Es una ''convención'' de codificación opcional. Dependiendo del uso es si es necesario o no. Personalmente creo que si no lo necesitas no debería estar allí. ¿Por qué incluir algo que no será utilizado o alcanzado por el usuario?
Si las posibilidades del caso son limitadas (es decir, un Booleano), la cláusula predeterminada es redundante .
NO tener el caso predeterminado puede ser beneficioso en algunas situaciones.
Si los casos de cambio son valores de enumeración, al no tener un caso predeterminado, puede obtener una advertencia del compilador si falta algún caso. De esa manera, si se agregan nuevos valores de enumeración en el futuro y se olvida de agregar casos para estos valores en el conmutador, puede averiguar sobre el problema en el momento de la compilación. Aún debe asegurarse de que el código realice la acción apropiada para los valores no manejados, en caso de que se haya convertido un valor no válido al tipo de enumeración. Por lo tanto, esto puede funcionar mejor para casos simples en los que puede regresar dentro del caso de enumeración en lugar de romper.
enum SomeEnum
{
ENUM_1,
ENUM_2,
// More ENUM values may be added in future
};
int foo(SomeEnum value)
{
switch (value)
{
case ENUM_1:
return 1;
case ENUM_2:
return 2;
}
// handle invalid values here
return 0;
}
No.
Lo que si no hay una acción por defecto, el contexto importa. ¿Y si solo te interesa actuar sobre unos pocos valores?
Tomemos el ejemplo de la lectura de teclas para un juego.
switch(a)
{
case ''w'':
// Move Up
break;
case ''s'':
// Move Down
break;
case ''a'':
// Move Left
break;
case ''d'':
// Move Right
break;
}
Añadiendo:
default: // Do nothing
Es solo una pérdida de tiempo y aumenta la complejidad del código sin ningún motivo.
Por lo que veo, la respuesta es "por defecto" es opcional, decir que un conmutador siempre debe contener un predeterminado es como decir que cada "if-elseif" debe contener un "else". Si hay una lógica que hacer por defecto, entonces la declaración ''por defecto'' debería estar allí, pero de lo contrario el código podría continuar ejecutándose sin hacer nada.
Porque MISRA C lo dice así:
El requisito para una cláusula por defecto final es la programación defensiva. Esta cláusula tomará la acción apropiada o contendrá un comentario adecuado sobre por qué no se toma ninguna acción.
Dicho esto, recomiendo no seguir a MISRA C en esto, para la mayoría de los programas:
- A esta guía de estilo de programación defensiva no le importa que solo algunos valores sean válidos. Si la variable es físicamente capaz de tomar un valor, incluso si eso fuera un error, se supone que debe manejarlo. La mayoría del software debería preferir imprimir un seguimiento de pila en lugar de errores de "manejo" (como lo menciona Ophir Yoktan).
- Los conmutadores de enumeración, en particular, no deberían tener una cláusula predeterminada (como dijo Harlan Kassler). Y, como lo demuestra Harlan también, el manejo de valores no válidos puede hacerse fuera del interruptor, un punto que faltaba en la discusión de Misra .
Si el valor de cambio ( switch (variable )) no puede alcanzar el caso predeterminado, entonces el caso predeterminado no es necesario en absoluto. Incluso si mantenemos el caso predeterminado, no se ejecuta en absoluto. Es código muerto.
Si no hay un caso predeterminado en una declaración de switch
, el comportamiento puede ser impredecible si ese caso surge en algún momento del tiempo, lo que no era predecible en la etapa de desarrollo. Es una buena práctica incluir un caso default
.
switch ( x ){
case 0 : { - - - -}
case 1 : { - - - -}
}
/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */
Una práctica de este tipo puede provocar errores como la anulación de NULL , la pérdida de memoria y otros tipos de errores graves .
Por ejemplo, suponemos que cada condición inicializa un puntero. Pero si se supone que surge un caso default
y si no se inicializa en este caso, existe toda posibilidad de encontrar una excepción de puntero nulo. Por lo tanto, se sugiere utilizar una declaración de caso default
, aunque puede ser trivial.
Si sabe que la instrucción de cambio solo tendrá un conjunto estricto de etiquetas o valores definidos, simplemente haga esto para cubrir las bases, de esa manera siempre obtendrá un resultado válido. Solo coloque el valor predeterminado sobre la etiqueta que programáticamente / lógicamente Ser el mejor manejador para otros valores.
switch(ResponseValue)
{
default:
case No:
return false;
case Yes;
return true;
}
Siempre usaría una cláusula predeterminada, sin importar en qué idioma esté trabajando.
Las cosas pueden y van mal. Los valores no serán lo que esperas, y así sucesivamente.
No querer incluir una cláusula predeterminada implica que está seguro de que conoce el conjunto de valores posibles. Si cree que conoce el conjunto de valores posibles, entonces, si el valor está fuera de este conjunto de valores posibles, desearía que se le informe, es sin duda un error.
Esa es la razón por la que siempre debe usar una cláusula predeterminada y lanzar un error, por ejemplo en Java:
switch (myVar) {
case 1: ......; break;
case 2: ......; break;
default: throw new RuntimeException("unreachable");
}
No hay razón para incluir más información que solo la cadena "inalcanzable"; Si realmente sucede, tendrá que mirar la fuente y los valores de las variables, etc. de todos modos, y la excepción stacktrace incluirá ese número de línea, por lo que no tendrá que perder el tiempo escribiendo más texto en el mensaje de excepción.
Tener una cláusula predeterminada cuando no es realmente necesaria es la programación defensiva Esto generalmente conduce a un código que es demasiado complejo debido a un código de manejo de errores excesivo. Este código de detección y manejo de errores perjudica la legibilidad del código, hace que el mantenimiento sea más difícil y eventualmente conduce a más errores de los que resuelve.
Así que creo que si no se debe alcanzar el valor predeterminado, no tiene que agregarlo.
Tenga en cuenta que "no se debe alcanzar" significa que si se alcanzó un error en el software, es necesario probar valores que pueden contener valores no deseados debido a la entrada del usuario, etc.
Los casos de cambio casi siempre deben tener un caso default
.
Razones para usar un default
1.A ''capturar'' un valor inesperado
switch(type)
{
case 1:
//something
case 2:
//something else
default:
// unknown type! based on the language,
// there should probably be some error-handling
// here, maybe an exception
}
2. Para manejar acciones ''predeterminadas'', donde los casos son por comportamiento especial.
Esto se ve mucho en los programas controlados por menú y en los scripts de shell bash. También puede ver esto cuando una variable se declara fuera del caso de conmutación pero no se inicializa, y cada caso se inicializa en algo diferente. Aquí el valor predeterminado debe inicializarlo también para que el código de línea que accede a la variable no genere un error.
3. Para mostrarle a alguien que lee tu código que has cubierto ese caso.
variable = (variable == "value") ? 1 : 2;
switch(variable)
{
case 1:
// something
case 2:
// something else
default:
// will NOT execute because of the line preceding the switch.
}
Este fue un ejemplo demasiado simplificado, pero el punto es que alguien que lee el código no debería preguntarse por qué la variable
no puede ser otra que 1 o 2.
El único caso en el que puedo pensar en NO usar el default
es cuando el interruptor está comprobando algo donde es obvio que cualquier otra alternativa puede ignorarse alegremente
switch(keystroke)
{
case ''w'':
// move up
case ''a'':
// move left
case ''s'':
// move down
case ''d'':
// move right
// no default really required here
}