Fallthrough sentencia switch es una de mis principales razones personales para amar switch
vs. if/else if
construcciones. Un ejemplo está en orden aquí:
static string NumberToWords(int number)
{
string[] numbers = new string[]
{ "", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine" };
string[] tens = new string[]
{ "", "", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety" };
string[] teens = new string[]
{ "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen" };
string ans = "";
switch (number.ToString().Length)
{
case 3:
ans += string.Format("{0} hundred and ", numbers[number / 100]);
case 2:
int t = (number / 10) % 10;
if (t == 1)
{
ans += teens[number % 10];
break;
}
else if (t > 1)
ans += string.Format("{0}-", tens[t]);
case 1:
int o = number % 10;
ans += numbers[o];
break;
default:
throw new ArgumentException("number");
}
return ans;
}
Las personas inteligentes se encogen porque las string[]
s deben declararse fuera de la función: bueno, lo son, este es solo un ejemplo.
El compilador falla con el siguiente error:
El control no puede pasar de una etiqueta de caso ('caso 3:') a otro El control no puede pasar de una etiqueta de caso ('caso 2:') a otro
¿Por qué? ¿Y hay alguna forma de obtener este tipo de comportamiento sin tener tres if
s?
c#
switch-statement
Matthew Scharley
fuente
fuente
El "por qué" es evitar la caída accidental, por lo que estoy agradecido. Esta es una fuente no común de errores en C y Java.
La solución alternativa es usar goto, p. Ej.
El diseño general de switch / case es un poco desafortunado en mi opinión. Se quedó demasiado cerca de C: hay algunos cambios útiles que podrían hacerse en términos de alcance, etc. Podría decirse que un interruptor más inteligente que podría hacer coincidir patrones, etc. sería útil, pero eso realmente está cambiando de interruptor a "verificar una secuencia de condiciones" - en ese punto quizás se requeriría un nombre diferente.
fuente
if (result = true) { }
Switch Fallthrough es históricamente una de las principales fuentes de errores en los softwares modernos. El diseñador de idiomas decidió hacer obligatorio el salto al final del caso, a menos que esté omitiendo el siguiente caso directamente sin procesamiento.
fuente
case 1, 2:
en los idiomas que lo permiten. Lo que nunca entenderé es por qué cualquier lenguaje moderno no elegiría permitir eso.case 1, 2:
es una etiqueta única pero con varios nombres. - FWIW, creo que la mayoría de los idiomas que prohíben la caída no hacen caso especial a las "etiquetas de caso consecutivas", sino que tratan las etiquetas de caso como una anotación en la siguiente declaración y requieren la última declaración antes de una declaración etiquetada con (uno o más) etiquetas de casos para ser un salto.Para agregar a las respuestas aquí, creo que vale la pena considerar la pregunta opuesta junto con esto, a saber. ¿Por qué C permitió la caída en primer lugar?
Cualquier lenguaje de programación, por supuesto, tiene dos objetivos:
La creación de cualquier lenguaje de programación es, por lo tanto, un equilibrio entre la mejor manera de servir estos dos objetivos. Por un lado, cuanto más fácil sea convertir las instrucciones de la computadora (ya sean códigos de máquina, bytecode como IL, o las instrucciones se interpretan en la ejecución), más capaz será ese proceso de compilación o interpretación para ser eficiente, confiable y compacto en salida. Llevado a su extremo, este objetivo da como resultado que solo escribamos en ensamblador, IL o incluso códigos de operación sin procesar, porque la compilación más fácil es donde no hay compilación en absoluto.
Por el contrario, cuanto más expresa el lenguaje la intención del programador, en lugar de los medios utilizados para ese fin, más comprensible es el programa tanto al escribir como durante el mantenimiento.
Ahora,
switch
siempre podría haberse compilado convirtiéndolo en la cadena deif-else
bloques equivalente o similar, pero fue diseñado para permitir la compilación en un patrón de ensamblaje común particular donde uno toma un valor, calcula un desplazamiento de él (ya sea buscando una tabla indexado por un hash perfecto del valor, o por aritmética real en el valor *). Vale la pena señalar en este punto que hoy, la compilación de C # a veces se convertiráswitch
en el equivalenteif-else
, y a veces utilizará un enfoque de salto basado en hash (y de la misma manera con C, C ++ y otros lenguajes con sintaxis comparable).En este caso, hay dos buenas razones para permitir la caída:
De todos modos, sucede de forma natural: si construye una tabla de salto en un conjunto de instrucciones, y uno de los lotes de instrucciones anteriores no contiene algún tipo de salto o retorno, la ejecución progresará naturalmente al siguiente lote. Permitir la caída era lo que "sucedería" si giraba el
switch
C en una tabla de salto, utilizando el código de máquina.Los codificadores que escribieron en conjunto ya estaban acostumbrados al equivalente: al escribir una tabla de salto a mano en conjunto, tendrían que considerar si un bloque de código dado terminaría con un retorno, un salto fuera de la tabla, o simplemente continuaría a la siguiente cuadra. Como tal, hacer que el codificador agregue un explícito
break
cuando sea necesario también fue "natural" para el codificador.En ese momento, por lo tanto, fue un intento razonable de equilibrar los dos objetivos de un lenguaje de computadora en lo que se refiere tanto al código de máquina producido como a la expresividad del código fuente.
Sin embargo, cuatro décadas después, las cosas no son exactamente iguales, por algunas razones:
switch
cualquiera se convierta enif-else
porque se consideró que el enfoque es más eficiente, o que se convierta en una variante particularmente esotérica del enfoque de la tabla de salto es mayor. El mapeo entre los enfoques de nivel superior e inferior no es tan fuerte como lo era antes.switch
bloques usaron una caída distinta de las etiquetas múltiples en el mismo bloque, y se pensó que el uso- caso aquí significaba que este 3% era, de hecho, mucho más alto de lo normal). Entonces, el lenguaje estudiado hace que lo inusual se atienda más fácilmente que lo común.En relación con esos dos últimos puntos, considere la siguiente cita de la edición actual de K&R:
Entonces, desde la boca del caballo, la caída en C es problemática. Se considera una buena práctica documentar siempre las fallas con comentarios, lo cual es una aplicación del principio general de que uno debe documentar dónde se hace algo inusual, porque eso es lo que hará que el examen posterior del código y / o haga que su código se vea así tiene un error de novato cuando de hecho es correcto.
Y cuando lo piensas, codifica así:
Es agregando algo para hacer que la caída sea explícita en el código, simplemente no es algo que el compilador pueda detectar (o cuya ausencia se pueda detectar).
Como tal, el hecho de que on tiene que ser explícito con fall-through en C # no agrega ninguna penalización a las personas que escribieron bien en otros lenguajes de estilo C de todos modos, ya que ya serían explícitos en sus fallos. †
Finalmente, el uso de
goto
here ya es una norma de C y otros lenguajes similares:En este tipo de caso en el que queremos que se incluya un bloque en el código ejecutado para un valor distinto de aquel que trae uno al bloque anterior, entonces ya tenemos que usarlo
goto
. (Por supuesto, hay medios y formas de evitar esto con diferentes condicionales, pero eso es cierto en casi todo lo relacionado con esta pregunta). Como tal, C # se basó en la forma ya normal de lidiar con una situación en la que queremos golpear más de un bloque de código en unswitch
, y simplemente lo generalizó para cubrir también la falla. También hizo que ambos casos fueran más convenientes y autodocumentados, ya que tenemos que agregar una nueva etiqueta en C pero podemos usarlacase
como etiqueta en C #. En C # podemos deshacernos de labelow_six
etiqueta y usarlagoto case 5
que es más claro en cuanto a lo que estamos haciendo. (También tendríamos que agregarbreak
para eldefault
, que dejé fuera solo para hacer que el código C anterior claramente no sea el código C #).En resumen, por lo tanto:
break
, para que los que estén familiarizados con lenguajes más sencillos aprendan el idioma y porten más fácilmente.goto
enfoque basado en la base para golpear el mismo bloque desde diferentescase
etiquetas como se usa en C. Simplemente lo generaliza a otros casos.goto
enfoque basado en la base sea más conveniente y más claro que en C, al permitir que lascase
declaraciones actúen como etiquetas.En definitiva, una decisión de diseño bastante razonable
* Algunas formas de BASIC le permitirían a uno hacer cosas similares,
GOTO (x AND 7) * 50 + 240
mientras que frágil y, por lo tanto, un caso particularmente persuasivo para la prohibicióngoto
, sirve para mostrar un equivalente en un idioma superior del tipo de forma en que el código de nivel inferior puede hacer un salto basado en aritmética sobre un valor, que es mucho más razonable cuando es el resultado de la compilación en lugar de algo que debe mantenerse manualmente. Las implementaciones del dispositivo de Duff en particular se prestan bien al código de máquina equivalente o IL porque cada bloque de instrucciones a menudo tendrá la misma longitud sin necesidad de agregarnop
rellenos.† El dispositivo de Duff vuelve a aparecer aquí, como una excepción razonable. El hecho de que con eso y patrones similares haya una repetición de operaciones sirve para hacer que el uso de la caída sea relativamente claro, incluso sin un comentario explícito al respecto.
fuente
Puede 'ir a la etiqueta del caso' http://www.blackwasp.co.uk/CSharpGoto.aspx
fuente
Dejaron de lado este comportamiento por diseño para evitar cuando no fue utilizado por voluntad pero causó problemas.
Se puede usar solo si no hay una declaración en la parte del caso, como:
fuente
Cambiaron el comportamiento de la declaración de cambio (de C / Java / C ++) para c #. Supongo que el razonamiento fue que la gente se olvidó de la caída y se causaron errores. Un libro que leí decía que usaba goto para simular, pero esto no me parece una buena solución.
fuente
goto case
está. Su ventaja sobre el fracaso es que es explícito. El hecho de que algunas personas se opongangoto case
simplemente demuestra que fueron adoctrinados contra el "goto" sin comprender el problema y que son incapaces de pensar por sí mismos. Cuando Dijkstra escribió "GOTO Considerado Dañino", se refería a idiomas que no tenían ningún otro medio para cambiar el flujo de control.goto
puede ser al optimizar el código?Puede lograr caer como C ++ por la palabra clave goto.
EX:
fuente
- Documentación de C # switch ()
fuente
Después de cada declaración de caso, se requiere una declaración de interrupción o goto , incluso si es un caso predeterminado.
fuente
Solo una nota rápida para agregar que el compilador de Xamarin realmente se equivocó y permite fallos. Supuestamente se ha solucionado, pero no se ha publicado. Descubrí esto en algún código que en realidad estaba fallando y el compilador no se quejó.
fuente
el interruptor (Referencia de C #) dice
Por lo tanto, también debe agregar un
break;
a sudefault
sección, de lo contrario seguirá habiendo un error del compilador.fuente
C # no admite fallos con declaraciones de cambio / caso. No estoy seguro de por qué, pero realmente no hay soporte para ello. Enlace
fuente
goto case
. Esta fue una elección de diseño intencional y sabia por parte de los diseñadores de C #.Olvidó agregar el "descanso"; declaración en el caso 3. En el caso 2 lo escribió en el bloque if. Por lo tanto, intente esto:
fuente