¿Cuál es el beneficio / desventaja de usar una switch
declaración vs. an if/else
en C #? No puedo imaginar que haya una diferencia tan grande, aparte de tal vez la apariencia de su código.
¿Hay alguna razón por la cual la IL resultante o el rendimiento del tiempo de ejecución asociado serían radicalmente diferentes?
Relacionado: ¿Qué es más rápido, activar cadena o elseif en tipo?
c#
.net
switch-statement
Matthew M. Osborn
fuente
fuente
Respuestas:
La instrucción SWITCH solo produce el mismo ensamblaje que los IF en modo de depuración o compatibilidad. En el lanzamiento, se compilará en la tabla de salto (a través de la declaració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 construir tablas de salto para cadenas de longitudes arbitrarias, por lo que la mayoría de las veces este 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 hash no es estrictamente O (1) y tiene costos constantes notables, pero si el número de etiquetas de mayúsculas y minúsculas es grande, será significativamente más rápido que comparar cada constante de cadena en los IF.
Para resumir, si el número de condiciones es más de 5 más o menos, prefiera CAMBIAR sobre SI, de lo contrario, use lo que se vea mejor.
fuente
En general (considerando todos los idiomas y todos los compiladores), una declaración de cambio PUEDE SER A VECES más eficiente que una declaración de if / else, porque es fácil para un compilador generar tablas de salto a partir de declaraciones de cambio. Es posible hacer lo mismo para 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 instrucción switch, porque el compilador usará una tabla hash para implementar el salto.
Con un pequeño número de cadenas, el rendimiento entre los dos 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 los bloques IF / ELSE.
Hay una instrucción MSIL de "declaración de cambio" que, cuando se modifique, utilizará una tabla de salto para implementar una declaración de cambio. Sin embargo, solo funciona con tipos enteros (esta pregunta se refiere a cadenas).
Para pequeñas cantidades de cadenas, es más eficiente que el compilador genere bloques IF / ELSE que usar una tabla hash.
Cuando noté esto originalmente, asumí que debido a que los bloques IF / ELSE se usaban con una pequeña cantidad de cadenas, el compilador hizo la misma transformación para grandes cantidades de cadenas.
Esto estaba mal. 'IMA' tuvo la amabilidad de señalarme esto (bueno ... él no fue amable al respecto, pero tenía razón y yo estaba equivocado, lo cual es la parte importante)
También hice una suposición descabellada sobre la falta de una instrucción de "cambio" en MSIL (supuse que, si había una primitiva de conmutación, ¿por qué no la usaban con una tabla hash? Por lo tanto, no debe haber una primitiva de conmutación). ...) Esto estaba 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 la publicación de 'ima'.
fuente
Tres razones para preferir el
switch
:Un compilador que apunta al 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 casos, se han escrito una gran cantidad de documentos aprendidos sobre cómo compilar declaraciones de casos de manera eficiente; algunos están vinculados desde la página del compilador de lcc . (Lcc tenía uno de los compiladores más innovadores para conmutadores).Una instrucción switch es una elección entre alternativas mutuamente excluyentes y la sintaxis del interruptor hace que este control fluya más transparente para el programador que un conjunto de instrucciones if-then-else.
En algunos idiomas, incluidos definitivamente ML y Haskell, el compilador verifica si ha omitido algún caso . Veo esta característica como una de las principales ventajas de ML y Haskell. No sé si C # puede hacer esto.
Una anécdota: en una conferencia que pronunció al recibir un premio por su 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:
case
declaración)No me puedo imaginar vivir sin él
switch
.fuente
El compilador va a optimizar casi 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 si las declaraciones encadenan juntas.
Los amigos no permiten que los amigos apilen declaraciones if-else.
fuente
En realidad, una declaración de cambio es más eficiente. El compilador lo optimizará a una tabla de búsqueda donde con sentencias if / else no puede. La desventaja es que una declaración de cambio no se puede usar con valores variables.
No puedes hacer:
tiene que ser
fuente
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 diversos casos sean aproximadamente igualmente probables. En los casos en que uno (o unos pocos) de los valores son mucho más probables, la escalera if-then-else puede ser mucho más rápida, asegurando que los casos más comunes se verifiquen primero:
Así por ejemplo:
vs
Si x es cero el 90% del tiempo, el código "if-else" puede ser el doble de rápido que el código basado en el interruptor. Incluso si el compilador convierte el "interruptor" en una especie de goto inteligente basado en tablas, no será tan rápido como simplemente verificar cero.
fuente
switch
compatibles, laswitch
declaración es mejor (más legible, a veces más rápida). Si usted sabe que uno de los casos es mucho más probable, se puede tirar de que fuera para formar unif
-else
-switch
construcción y si es mensurable más rápido , dejar que en (Repetir, si es necesario.) De la OMI que todavía es razonablemente fácil de leer.. Si elswitch
degenera y se vuelve demasiado pequeño, un reemplazo de expresiones regulares hará la mayor parte del trabajo de transformarlo en unaelse if
cadena.a menudo se verá mejor, es decir, será más fácil entender lo que está sucediendo. Teniendo en cuenta que el beneficio de rendimiento será extremadamente mínimo en el mejor de los casos, la vista del código es la diferencia más importante.
Entonces, si el if / else se ve mejor, úselo, de lo contrario use una instrucción switch.
fuente
Tema secundario, pero a menudo me preocupo por (y más a menudo veo)
if
/else
y laswitch
declaración se vuelve demasiado grande con demasiados casos. Estos a menudo perjudican la mantenibilidad.Los culpables comunes incluyen:
Arreglar:
fuente
Según este enlace, la comparación IF vs Switch de la prueba de iteración usando la instrucción switch y if, es como para 1,000,000,000 de iteraciones, el tiempo que toma la declaración Switch = 43.0s y la declaración If = 48.0s
Que es literalmente 20833333 iteraciones por segundo. Entonces, ¿realmente deberíamos enfocarnos más?
PD: Solo para saber la diferencia de rendimiento para una pequeña lista de condiciones.
fuente
Si solo está usando la declaración if o else, la solución base está usando la comparación. operador
Puedes hacer la rutina en un interruptor
fuente
En realidad, esto no responde a su pregunta, pero dado que habrá poca diferencia entre las versiones compiladas, le insto a que escriba su código de la manera que mejor describa sus intenciones. No solo hay una mejor oportunidad de que el compilador haga lo que espera, sino que facilitará que otros mantengan su código.
Si su intención es bifurcar su programa en función del valor de una variable / atributo, una declaración de cambio representa mejor esa intención.
Si su intención es ramificar su programa en función de diferentes variables / atributos / condiciones, entonces una cadena if / else if representa mejor esa intención.
Admitiré que Cody tiene razón acerca de que las personas olvidan el comando de interrupción, pero casi con la misma frecuencia veo que las personas se complican si los bloques obtienen el {} incorrecto, por lo que las líneas que deberían estar en la declaración condicional no lo son. Es una de las razones por las que siempre incluyo {} en mis declaraciones if, incluso si hay una línea en él. No solo es más fácil de leer, sino que si necesito agregar otra línea en el condicional, no puedo olvidar agregarlo.
fuente
Pregunta de interés Esto surgió hace unas semanas en el trabajo y encontramos una respuesta escribiendo un fragmento de ejemplo y viéndolo en .NET Reflector (¡el reflector es increíble! ¡Me encanta!).
Esto es lo que descubrimos: una declaración de cambio válida para cualquier cosa que no sea una cadena se compila en IL como una declaración de cambio. Sin embargo, si es una cadena, se reescribe como if / else if / else en IL. Entonces, en nuestro caso, queríamos saber cómo las declaraciones de cambio comparan cadenas, por ejemplo, distinguen entre mayúsculas y minúsculas, etc. y el reflector rápidamente nos dio una respuesta. Esto fue útil para saber.
Si desea hacer una comparación entre mayúsculas y minúsculas en las cadenas, puede usar una instrucción de cambio, ya que es más rápido que realizar una Cadena. Compare en un if / else. (Editar: Lea ¿Qué es más rápido, encienda la cadena o si no en el tipo? Para algunas pruebas de rendimiento reales) Sin embargo, si desea hacer una distinción entre mayúsculas y minúsculas, es mejor usar un if / else ya que el código resultante no es bonito.
La mejor regla general es usar declaraciones de cambio si tiene sentido (en serio), por ejemplo:
Si necesita manipular el valor para alimentar la declaración de cambio (crear una variable temporal para cambiar), entonces probablemente debería estar usando una declaración de control if / else.
Una actualización:
En realidad, es mejor convertir la cadena a mayúsculas (por ejemplo
ToUpper()
), ya que aparentemente ha habido otras optimizaciones que el compilador just-in-time puede hacer en comparación conToLower()
. Es una micro optimización, sin embargo, en un ciclo cerrado podría ser útil.Una pequeña nota al margen:
Para mejorar la legibilidad de las declaraciones de switch, intente lo siguiente:
fuente
La declaración de cambio es definitivamente la más rápida que un if if if. Hay pruebas de velocidad que BlackWasp le ha proporcionado.
http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
--Echale un vistazo
Pero depende en gran medida de las posibilidades que está tratando de explicar, pero trato de usar una declaración de cambio siempre que sea posible.
fuente
No solo C #, sino todos los lenguajes basados en C, creo: debido a que un interruptor está limitado a constantes, es posible generar código muy eficiente usando una "tabla de salto". El caso C es realmente un viejo GOTO calculado por FORTRAN, pero el caso C # todavía se prueba contra una constante.
No es el caso de que el optimizador pueda hacer el mismo código. Considere, por ejemplo,
Debido a que son booleanos compuestos, el código generado tiene que calcular un valor y un cortocircuito. Ahora considera el equivalente
Esto se puede compilar en
porque implícitamente le está diciendo al compilador que no necesita calcular las pruebas de OR e igualdad.
fuente
Mi profesor de CS sugirió no cambiar las declaraciones, ya que a menudo la gente olvidaba el descanso o lo usaba incorrectamente. No puedo recordar exactamente lo que dijo, pero algo similar a mirar una base de código seminal que mostraba ejemplos de la declaración de cambio (hace años) también tenía toneladas de errores.
fuente
¡Algo que acabo de notar es que puedes combinar if / else y cambiar declaraciones! Muy útil cuando se necesita verificar condiciones previas.
fuente
Creo que Switch es más rápido que si Condiciones como ver si hay un programa como:
Escriba un programa para ingresar cualquier número (entre 1 y 99) y verifique en qué ranura a) 1 - 9 luego ranura uno b) 11-19 y luego ranura dos c) 21-29 luego ranura tres y así hasta 89- 99
Luego, si tiene que hacer muchas condiciones, pero la caja del interruptor de hijo debe escribir
será tan fácil
¡Hay muchos más ejemplos de este tipo también!
fuente
Una declaración de cambio básicamente es una comparación para la igualdad. los eventos de teclado tienen una gran ventaja sobre las instrucciones de cambio cuando tienen un código fácil de escribir y leer y luego una instrucción if elseif, perder un {soporte} también podría ser problemático.
Una declaración if elseif es ideal para más de una solución si (theAmountOfApples es mayor que 5 && theAmountOfApples es menor que 10) guarde sus manzanas más if (theAmountOfApples es mayor que 10 || theAmountOfApples == 100) venda sus manzanas. No escribo C # o C ++, pero lo aprendí antes de aprender Java y son idiomas cercanos.
fuente
Una posible desventaja de las declaraciones de cambio es su falta de múltiples condiciones. Puede tener múltiples condiciones para el if (else) pero no múltiples declaraciones de casos con diferentes condiciones en un switch.
Las declaraciones de cambio no son adecuadas para operaciones lógicas más allá del alcance de ecuaciones / expresiones booleanas simples. Para esas ecuaciones / expresiones booleanas, es eminentemente adecuado pero no para otras operaciones lógicas.
Usted tiene mucha más libertad con la lógica disponible en las declaraciones If, pero la legibilidad puede verse afectada si la declaración If se vuelve difícil de manejar o se maneja mal.
Ambos tienen lugar según el contexto de lo que se enfrenta.
fuente