¿Existe alguna diferencia significativa entre usar if/else y switch-case en C#?
¿Cuál es el beneficio/desventaja de usar una switch
declaración frente a una if/else
en C#? No puedo imaginar que haya una diferencia tan grande, aparte tal vez del aspecto de su código.
¿Existe alguna razón por la cual el IL resultante o el rendimiento del tiempo de ejecución asociado serían radicalmente diferentes?
Relacionado: ¿Qué es más rápido, activar la cadena o si no el tipo?
La declaración SWITCH solo produce el mismo ensamblaje que los IF en modo de depuración o compatibilidad. En el lanzamiento, se compilará en una tabla de salto (a través de la instrucción 'switch' de MSIL), que es O(1).
C# (a diferencia de muchos otros lenguajes) también permite activar constantes de cadena, y esto funciona de manera un poco diferente. Obviamente, no es práctico crear tablas de salto para cadenas de longitudes arbitrarias, por lo que la mayoría de las veces dicho cambio se compilará en una pila de IF.
Pero si el número de condiciones es lo suficientemente grande como para cubrir los gastos generales, el compilador de C# creará un objeto HashTable, lo completará con constantes de cadena y realizará una búsqueda en esa tabla seguida de un salto. La búsqueda de tabla hash no es estrictamente O(1) y tiene costos constantes notables, pero si el número de etiquetas de casos es grande, será significativamente más rápido que comparar cada constante de cadena en IF.
En resumen, si el número de condiciones es superior a 5 aproximadamente, prefiera SWITCH sobre IF; de lo contrario, utilice lo que se vea mejor.
En general (considerando todos los lenguajes y todos los compiladores), una declaración de cambio PUEDE A VECES ser más eficiente que una declaración if/else, porque es fácil para un compilador generar tablas de salto a partir de declaraciones de cambio. Es posible hacer lo mismo con las declaraciones if/else, dadas las restricciones apropiadas, pero eso es mucho más difícil.
En el caso de C#, esto también es cierto, pero por otras razones.
Con una gran cantidad de cadenas, existe una ventaja de rendimiento significativa al usar una declaración de cambio, porque el compilador usará una tabla hash para implementar el salto.
Con un número reducido de cuerdas, el rendimiento entre ambas es el mismo.
Esto se debe a que en ese caso el compilador de C# no genera una tabla de salto. En su lugar, genera MSIL que es equivalente a bloques IF/ELSE.
Hay una instrucción MSIL de "declaración de cambio" que, cuando se activa, utilizará una tabla de salto para implementar una declaración de cambio. Sin embargo, solo funciona con tipos de números enteros (esta pregunta es sobre cadenas).
Para un número pequeño de cadenas, es más eficiente que el compilador genere bloques IF/ELSE que usar una tabla hash.
Cuando me di cuenta de esto originalmente, supuse que debido a que los bloques IF/ELSE se usaban con una pequeña cantidad de cadenas, el compilador hacía la misma transformación para una gran cantidad de cadenas.
Esto estuvo MAL. 'IMA' tuvo la amabilidad de señalarme esto (bueno... él no fue amable con eso, pero él tenía razón y yo estaba equivocado, que es la parte importante)
También hice una suposición descabellada sobre la falta de una instrucción de "cambio" en MSIL (pensé que, si había una primitiva de cambio, ¿por qué no la usaban con una tabla hash, por lo que no debe haber una primitiva de cambio? ... ). Esto estuvo mal e increíblemente estúpido de mi parte. Nuevamente 'IMA' me señaló esto.
Hice las actualizaciones aquí porque es la publicación mejor calificada y la respuesta aceptada.
Sin embargo, lo hice Community Wiki porque creo que no merezco el REP por estar equivocado. Si tienes la oportunidad, vota por la publicación de 'ima.
El compilador optimizará prácticamente todo en el mismo código con pequeñas diferencias (Knuth, ¿alguien?).
La diferencia es que una declaración de cambio es más limpia que quince declaraciones si no están unidas.
Los amigos no permiten que sus amigos acumulen declaraciones if-else.
No vi a nadie más plantear el punto (¿obvio?) de que la supuesta ventaja de eficiencia de la declaración de cambio depende de que los distintos casos sean aproximadamente igualmente probables. En los casos en los que uno (o algunos) de los valores son mucho más probables, la escalera if-then-else puede ser mucho más rápida, al garantizar que los casos más comunes se verifiquen primero:
Así por ejemplo:
if (x==0) then {
// do one thing
} else if (x==1) {
// do the other thing
} else if (x==2) {
// do the third thing
}
vs
switch(x) {
case 0:
// do one thing
break;
case 1:
// do the other thing
break;
case 2:
// do the third thing
break;
}
Si x es cero el 90% de las veces, el código "if-else" puede ser dos veces más rápido que el código basado en conmutador. Incluso si el compilador convierte el "interruptor" en una especie de elemento inteligente controlado por tabla, no será tan rápido como simplemente verificar si hay cero.
Tres razones para preferir switch
:
Un compilador dirigido a código nativo a menudo puede compilar una declaración de cambio en una rama condicional más un salto indirecto , mientras que una secuencia de
if
s requiere una secuencia de ramas condicionales . Dependiendo de la densidad de los casos, se han escrito muchos artículos eruditos sobre cómo compilar enunciados de casos de manera eficiente; algunos están vinculados desde la página del compilador lcc . (Lcc tenía uno de los compiladores para conmutadores más innovadores).Una declaración de cambio es una elección entre alternativas mutuamente excluyentes y la sintaxis de cambio hace que este flujo de control sea más transparente para el programador que un conjunto de declaraciones if-then-else.
En algunos lenguajes, incluidos definitivamente ML y Haskell, el compilador verifica si se ha omitido algún caso . Considero que esta característica es una de las principales ventajas de ML y Haskell. No sé si C# puede hacer esto.
Una anécdota: en una conferencia que dio al recibir un premio a la trayectoria, escuché a Tony Hoare decir que de todas las cosas que hizo en su carrera, había tres de las que estaba más orgulloso:
- Inventar Quicksort
- Inventar la declaración de cambio (que Tony llamó
case
declaración) - Inicio y fin de su carrera en la industria.
No puedo imaginarme vivir sinswitch
.