¿Hay alguna diferencia significativa entre usar if / else y switch-case en C #?

¿Cuál es el beneficio / desventaja de usar una instrucción switch frente a un if/else en C #. No puedo imaginar que haya tanta diferencia, aparte de tal vez el aspecto de tu código.

¿Hay alguna razón por la cual el IL resultante o el rendimiento del tiempo de ejecución asociado sería radicalmente diferente?

Relacionado: ¿Qué es más rápido, encienda la cadena o elseif en el tipo?

La sentencia SWITCH solo produce el mismo ensamblaje que IF en modo de depuración o compatibilidad. En la versión, se comstackrá en la tabla de salto (a través de la instrucción ‘switch’ de MSIL), que es O (1).

C # (a diferencia de muchos otros idiomas) también permite activar las constantes de cadena, y esto funciona de forma 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 dicho cambio se comstackrá en una stack de IF.

Pero si la cantidad de condiciones es lo suficientemente grande como para cubrir gastos generales, el comstackdor de C # creará un objeto HashTable, lo rellenará con constantes de cadena y realizará una búsqueda en esa tabla seguida de un salto. La búsqueda Hashtable no es estrictamente O (1) y tiene costos constantes constantes, pero si el número de tags de caja es grande, será significativamente más rápido que la comparación con cada constante de cadena en IF.

Para resumir, si el número de condiciones es más de 5 o más, prefiera SWITCH sobre IF, de lo contrario use lo que se vea mejor.

En general (considerando todos los lenguajes y todos los comstackdores) una sentencia switch PUEDE SER VECES más eficiente que una instrucción if / else, porque es fácil para un comstackdor generar tablas de salto a partir de sentencias switch. Es posible hacer lo mismo para las sentencias 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, hay una ventaja significativa en el rendimiento al usar una instrucción switch, porque el comstackdor utilizará 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 comstackdor de C # no genera una tabla de salto. En cambio, genera MSIL que es equivalente a bloques IF / ELSE.

Hay una instrucción MSIL de “instrucción de conmutación” que cuando se hace jitomate usará una tabla de salto para implementar una statement de cambio. Sin embargo, solo funciona con tipos enteros (esta pregunta es sobre cadenas).

Para pequeñas cantidades de cadenas, es más eficiente para el comstackdor generar bloques IF / ELSE y luego usar una tabla hash.

Cuando originalmente me di cuenta de esto, asumí que debido a que los bloques IF / ELSE se usaban con un pequeño número de cadenas, el comstackdor hacía la misma transformación para un gran número de cadenas.

Esto fue INCORRECTO ‘IMA’ tuvo la amabilidad de señalarme esto (bueno … no fue amable al respecto, pero tenía razón, y yo estaba equivocado, que es la parte importante)

También hice una suposición sobre la falta de una instrucción de “cambio” en MSIL (pensé, si había una primitiva de interruptor, ¿por qué no la estaban usando con una tabla hash, por lo que no debe haber una primitiva de cambio? …). Esto fue incorrecto e increíblemente estúpido de mi parte. Nuevamente ‘IMA’ me señaló esto.

Hice las actualizaciones aquí porque es la publicación mejor valorada y la respuesta aceptada.

Sin embargo, lo he convertido en Community Wiki porque creo que no merezco que el REP esté equivocado. Si tienes la oportunidad, sube la publicación de ‘ima’.

Tres razones para preferir el switch :

  • Un comstackdor que se dirige a un código nativo a menudo puede comstackr una instrucción switch en una twig condicional más un salto indirecto, mientras que una secuencia de if s requiere una secuencia de twigs condicionales . Dependiendo de la densidad de los casos, se han escrito muchos documentos aprendidos sobre cómo comstackr las declaraciones de casos de manera eficiente; algunos están vinculados desde la página del comstackdor lcc . (Lcc tenía uno de los comstackdores más innovadores para los interruptores).

  • Una instrucción switch es una elección entre alternativas mutuamente excluyentes y la syntax del switch hace que este flujo de control sea más transparente para el progtwigdor que un nido de declaraciones if-then-else.

  • En algunos idiomas, incluidos definitivamente ML y Haskell, el comstackdor comprueba 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 dio 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:

  • Inventando Quicksort
  • Inventar la statement de cambio (que Tony llamó la statement de case )
  • Comenzando y terminando su carrera en la industria

No puedo imaginar vivir sin switch .

En realidad, una statement de cambio es más eficiente. El comstackdor lo optimizará a una tabla de búsqueda donde con sentencias if / else no puede. El inconveniente es que una statement de cambio no se puede usar con valores variables.
No puedes hacer:

 switch(variable) { case someVariable break; default: break; } 

tiene que ser

 switch(variable) { case CONSTANT_VALUE; break; default: break; } 

El comstackdor optimizará casi todo en el mismo código con pequeñas diferencias (Knuth, ¿alguien?).

La diferencia es que una instrucción switch es más limpia que quince sentencias if else colgadas juntas.

Los amigos no permiten que los amigos apilen declaraciones if-else.

No vi a nadie más plantear el (¿obvio?) Punto de que la supuesta ventaja de eficiencia de la statement del interruptor depende de que los diversos casos sean aproximadamente igualmente probables. En los casos en que uno (o algunos) 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:

 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% del tiempo, el código “if-else” puede ser el doble de rápido que el código basado en el conmutador. Incluso si el comstackdor convierte el “interruptor” en algún tipo de goto controlado por tabla, aún así no será tan rápido como simplemente comprobar cero.

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.

Tema aparte, pero a menudo me preocupo (y más a menudo veo) if / else y la statement de switch vuelven demasiado grandes con demasiados casos. Estos a menudo duelen la facilidad de mantenimiento.

Los culpables comunes incluyen:

  1. Hacer demasiado dentro de múltiples sentencias if
  2. Más declaraciones de casos que humanamente posible analizar
  3. Demasiadas condiciones en la evaluación if para saber qué se está buscando

Arreglar:

  1. Extracto de refactorización de métodos.
  2. Utilice un diccionario con punteros de método en lugar de un caso, o use un IoC para mayor capacidad de configuración. Las fábricas de métodos también pueden ser útiles.
  3. Extrae las condiciones a su propio método

Si solo está utilizando la instrucción if o else, la solución base está utilizando la comparación? operador

 (value == value1) ? (type1)do this : (type1)or do this; 

Puede hacer la o la rutina en un interruptor

 switch(typeCode) { case TypeCode:Int32: case TypeCode.Int64: //dosomething here break; default: return; } 

Esto en realidad no responde su pregunta, pero dado que habrá poca diferencia entre las versiones comstackdas, lo instaría a que escriba su código de la manera que mejor describa sus intenciones. No solo existe una mejor posibilidad de que el comstackdor haga lo que espera, sino que hará que sea más fácil para otros mantener su código.

Si su intención es dividir su progtwig basado en el valor de una variable / atributo, entonces una statement de cambio representa mejor esa intención.

Si su intención es dividir su progtwig en función de diferentes variables / atributos / condiciones, entonces una cadena if / else if representa mejor esa intención.

Reconozco que cody tiene razón sobre las personas que olvidan el comando break, 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 el enunciado 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 ella. No solo es más fácil de leer, pero si necesito agregar otra línea en el condicional, no puedo olvidar agregarlo.

La statement de cambio es definitivamente más rápida que un if if si. Hay speedtest que han sido suministrados por BlackWasp

http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

–Echale un vistazo

Pero depende en gran medida de las posibilidades que intentas explicar, pero trato de usar una statement de cambio siempre que sea posible.

No solo C #, sino todos los lenguajes basados ​​en C, creo: como 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 # sigue siendo pruebas contra una constante.

No es el caso que el optimizador pueda hacer el mismo código. Considera, por ejemplo,

 if(a == 3){ //... } else if (a == 5 || a == 7){ //... } else {//... } 

Como esos son booleanos compuestos, el código generado tiene que calcular un valor y un cortocircuito. Ahora considere el equivalente

 switch(a){ case 3: // ... break; case 5: case 7: //... break; default: //... } 

Esto puede ser comstackdo en

 BTABL: * B3: addr of 3 code B5: B7: addr of 5,7 code load 0,1 ino reg X based on value jump indirect through BTABL+x 

porque está diciendo implícitamente al comstackdor que no necesita calcular las pruebas de igualdad e igualdad.

Pregunta de interés Esto apareció 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 sentencia de cambio válida para cualquier cosa que no sea una cadena se comstack en IL como una statement de cambio. Sin embargo, si se trata de una cadena, se reescribe como if / else if / else en IL. Entonces, en nuestro caso, queríamos saber cómo las instrucciones de cambio comparan cadenas, por ejemplo, distingue entre mayúsculas y minúsculas, y el reflector nos dio una respuesta rápidamente. Esto fue útil para saber

Si desea hacer una comparación sensible a las mayúsculas y minúsculas en las cadenas, podría utilizar una instrucción switch porque es más rápida que realizar un String.Compare en un if / else. (Editar: Lea lo que es más rápido, encienda la cadena o else en el tipo? Para algunas pruebas de rendimiento real) 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.

 switch (myString.ToLower()) { // not a good solution } 

La mejor regla de oro es usar declaraciones de cambio si tiene sentido (en serio), por ejemplo:

  • mejora la legibilidad de tu código
  • está comparando un rango de valores (float, int) o una enumeración

Si necesita manipular el valor para alimentar la instrucción switch (crear una variable temporal para cambiar) entonces probablemente debería usar una instrucción de control if / else.

Una actualización:

En realidad, es mejor convertir la cadena en mayúscula (por ejemplo, ToUpper() ), ya que aparentemente ha habido más optimizaciones que el comstackdor just-in-time puede hacer cuando se compara con ToLower() . Es una micro optimización, pero en un circuito cerrado podría ser útil.


Una pequeña nota al margen:

Para mejorar la legibilidad de las declaraciones de interruptor, intente lo siguiente:

  • poner la twig más probable primero, es decir, más accesible
  • si es probable que ocurran, enumérelos en orden alfabético para que sea más fácil encontrarlos.
  • nunca use el catch-all predeterminado para la última condición restante, eso es flojo y causará problemas más adelante en la vida del código.
  • use el catch-all predeterminado para afirmar una condición desconocida aunque sea muy poco probable que ocurra. eso es lo que afirma es bueno para.

Según este enlace, IF vs Switch comparación de la prueba de iteración usando switch y if, es como para 1,000,000,000 de iteraciones, Tiempo tomado por Switch Statement = 43.0s & by If Statement = 48.0s

Que es literalmente 20833333 iteraciones por segundo, Entonces, ¿Realmente deberíamos enfocarnos más,

PD: solo para conocer la diferencia de rendimiento para una lista pequeña de condiciones.

Mi profesor de cs sugirió no cambiar las declaraciones ya que a menudo las personas olvidaron el descanso o lo usaron incorrectamente. No puedo recordar exactamente lo que dijo, pero algo en el sentido de que al mirar una base de código seminal que mostraba ejemplos de la statement de cambio (hace años) también tenía un montón de errores.

¡Algo que acabo de notar es que puedes combinar if / else y cambiar las declaraciones! Muy útil cuando necesita verificar las condiciones previas.

 if (string.IsNullOrEmpty(line)) { //skip empty lines } else switch (line.Substring(0,1)) { case "1": Console.WriteLine(line); break; case "9": Console.WriteLine(line); break; default: break; } 

Creo que el cambio es más rápido que si las condiciones como ver si hay un progtwig como:

Escriba un progtwig para ingresar cualquier número (entre 1 – 99) y verifique en qué ranura a) 1 – 9, luego la ranura uno b) 11 – 19 luego la ranura dos c) 21-29 luego la ranura tres y así sucesivamente hasta 89- 99

Luego, si tiene que hacer muchas condiciones, pero el caso de cambio de hijo solo tiene que escribir

Cambiar (no / 10)

y en el caso 0 = 1-9, caso 1 = 11-19 y así sucesivamente

será tan fácil

¡Hay muchos más ejemplos de este tipo también!

una statement de cambio básicamente es una comparación para la igualdad. los eventos de teclado tienen una gran ventaja sobre los enunciados de conmutación cuando se tiene un código fácil de escribir y leer, y luego una sentencia if else, sin un paréntesis podría ser problemático.

 char abc; switch(abc) { case a: break; case b: break; case c: break; case d: break; } 

Una sentencia if elseif es excelente para más de una solución if (theAmountOfApples es mayor que 5 && theAmountOfApples es menor que 10) guarda tus manzanas else if (theAmountOfApples es mayor que 10 || theAmountOfApples == 100) venden tus manzanas. No escribo c # o c ++ pero lo aprendí antes de aprender java y son lenguajes cercanos.

Sé que esta no es exactamente la pregunta, pero realmente necesito señalar que cuando piensas en la eficiencia en ese nivel, es posible que necesites más abstracción en tu código. Ya no necesitará cajas de conmutadores, especialmente si contiene lógica. (mi ejemplo en php).

  $feeMapping = [ 1000 => 1, 2000 => 2, 3000 => 3, 4000 => 4, 5000 => 5, 6000 => 6, 7000 => 7 ]; function findFee($feeMapping, $amount) { foreach ($feeMapping as $fee => $value) { if ($value >= $amount) { return $fee; } } return 7; } $feeValue = findFee($feeMapping, 200); 

¡Ahora mira la redundancia del código similar!

  if ($amount >= 1000) { return 1; } elseif ($amount >= 2000) { return 2; } elseif ($amount >= 3000) { return 3; } elseif ($amount >= 4000) { return 4; } elseif ($amount >= 5000) { return 5; } elseif ($amount >= 6000) { return 6; } else { return 7; } 

Una posible desventaja de las declaraciones de cambio es su falta de múltiples condiciones. Puede tener varias condiciones para las sentencias if (else) pero no para múltiples casos con diferentes condiciones en un switch.

Las sentencias Switch no son adecuadas para operaciones lógicas más allá del scope de ecuaciones / expresiones booleanas simples. Para esas ecuaciones / expresiones booleanas, es eminentemente adecuado pero no para otras operaciones lógicas.

Tiene mucha más libertad con la lógica disponible en las sentencias If, pero la legibilidad puede verse afectada si la statement If se vuelve difícil de manejar o se maneja mal.

Ambos tienen lugar dependiendo del contexto al que se enfrentan.