Qual é a vantagem / desvantagem de usar uma switch
instrução vs. uma if/else
em C #. Não consigo imaginar a diferença, além da aparência do seu código.
Existe alguma razão pela qual a IL resultante ou o desempenho de tempo de execução associado seja radicalmente diferente?
Relacionado: O que é mais rápido, ativar a string ou o tipo?
c#
.net
switch-statement
Matthew M. Osborn
fonte
fonte
Respostas:
A instrução SWITCH produz apenas o mesmo assembly que os IFs no modo de depuração ou compatibilidade. No lançamento, ele será compilado na tabela de salto (através da instrução 'switch' do MSIL) - que é O (1).
O C # (diferente de muitos outros idiomas) também permite ativar constantes de string - e isso funciona de maneira um pouco diferente. Obviamente, não é prático criar tabelas de salto para sequências de comprimentos arbitrários, portanto, na maioria das vezes, esse comutador será compilado na pilha de IFs.
Mas se o número de condições for grande o suficiente para cobrir as despesas gerais, o compilador C # criará um objeto HashTable, preenchê-lo-á com constantes de seqüência de caracteres e fará uma pesquisa nessa tabela seguida de salto. A pesquisa da tabela de hash não é estritamente O (1) e possui custos constantes perceptíveis, mas se o número de rótulos de maiúsculas for grande, será significativamente mais rápido que comparar com cada constante de string nos FIs.
Para resumir, se o número de condições for superior a 5, prefira SWITCH ao IF, caso contrário, use o que parecer melhor.
fonte
Em geral (considerando todos os idiomas e todos os compiladores), uma instrução switch pode às vezes ser mais eficiente que uma instrução if / else, porque é fácil para um compilador gerar tabelas de salto a partir das instruções switch. É possível fazer o mesmo com instruções if / else, dadas as restrições apropriadas, mas isso é muito mais difícil.
No caso de C #, isso também é verdade, mas por outros motivos.
Com um grande número de cadeias, há uma vantagem significativa de desempenho ao usar uma instrução switch, porque o compilador usará uma tabela de hash para implementar o salto.
Com um pequeno número de strings, o desempenho entre os dois é o mesmo.
Isso ocorre porque, nesse caso, o compilador C # não gera uma tabela de salto. Em vez disso, gera MSIL equivalente aos blocos IF / ELSE.
Existe uma instrução MSIL "switch statement" que, quando acionada, usa uma tabela de salto para implementar uma instrução switch. Porém, ele funciona apenas com tipos inteiros (esta pergunta pergunta sobre cadeias).
Para um pequeno número de strings, é mais eficiente para o compilador gerar blocos IF / ELSE do que usar uma tabela de hash.
Quando percebi isso originalmente, assumi que, como os blocos IF / ELSE eram usados com um pequeno número de cadeias, o compilador fez a mesma transformação para um grande número de cadeias.
Isso foi errado. 'IMA' teve a gentileza de apontar isso para mim (bem ... ele não foi gentil com isso, mas ele estava certo, e eu estava errado, que é a parte importante)
Também fiz uma suposição sobre a falta de uma instrução "switch" no MSIL (imaginei que, se havia um primitivo de switch, por que eles não o estavam usando com uma tabela de hash, portanto, não deve haver um primitivo de switch). ...) Isso foi errado e incrivelmente estúpido da minha parte. Mais uma vez 'IMA' apontou isso para mim.
Fiz as atualizações aqui porque é a postagem com a melhor classificação e a resposta aceita.
No entanto, eu criei o Community Wiki porque acho que não mereço o REP por estar errado. Se você tiver uma chance, vote na publicação de 'ima'.
fonte
Três razões para preferir o
switch
:Um compilador direcionado para código nativo geralmente pode compilar uma instrução switch em uma ramificação condicional mais um salto indireto, enquanto uma sequência de
if
s requer uma sequência de ramificações condicionais . Dependendo da densidade dos casos, muitos artigos aprendidos foram escritos sobre como compilar declarações de casos com eficiência; alguns estão vinculados a partir da página do compilador lcc . (A Lcc tinha um dos compiladores mais inovadores para switches.)Uma instrução switch é uma escolha entre alternativas mutuamente exclusivas e a sintaxe do switch torna esse controle mais transparente para o programador do que um ninho de instruções if-then-else.
Em alguns idiomas, incluindo definitivamente o ML e o Haskell, o compilador verifica se você deixou algum caso de fora . Eu vejo esse recurso como uma das principais vantagens do ML e Haskell. Não sei se o C # pode fazer isso.
Uma anedota: em uma palestra que ele deu ao receber um prêmio pela conquista da vida, ouvi Tony Hoare dizer que, de todas as coisas que ele fez em sua carreira, havia três das quais ele mais se orgulhava:
case
instrução)Não consigo imaginar viver sem
switch
.fonte
O compilador otimizará praticamente tudo no mesmo código com pequenas diferenças (Knuth, alguém?).
A diferença é que uma instrução switch é mais limpa que quinze, se outras instruções estiverem juntas.
Amigos não deixam amigos empilhar declarações if-else.
fonte
Na verdade, uma instrução switch é mais eficiente. O compilador irá otimizá-lo para uma tabela de consulta onde, com instruções if / else, ele não pode. O lado negativo é que uma instrução switch não pode ser usada com valores variáveis.
Você não pode fazer:
tem que ser
fonte
Não vi ninguém mais levantar o ponto (óbvio?) De que a suposta vantagem de eficiência da instrução switch depende dos vários casos serem aproximadamente igualmente prováveis. Nos casos em que um (ou alguns) dos valores são muito mais prováveis, a escada if-then-else pode ser muito mais rápida, garantindo que os casos mais comuns sejam verificados primeiro:
Então, por exemplo:
vs
Se x for zero 90% do tempo, o código "if-else" pode ser duas vezes mais rápido que o código baseado em comutador. Mesmo se o compilador transformar o "switch" em algum tipo inteligente de controle de tabela, ele ainda não será tão rápido quanto simplesmente verificar zero.
fonte
switch
compatíveis, aswitch
declaração será melhor (mais legível, às vezes mais rápida). Se você sabe que um caso é muito mais provável, pode retirá-lo para formar uma construçãoif
-else
-switch
e, se for mensurável , é mais rápido . Deixe isso para dentro. (Repita, se necessário.) Se aswitch
degeneração for muito pequena, uma substituição de regex fará a maior parte do trabalho de transformá-la em umaelse if
cadeia.freqüentemente parecerá melhor - ou seja, será mais fácil entender o que está acontecendo. Considerando que o benefício de desempenho será extremamente mínimo, na melhor das hipóteses, a visualização do código é a diferença mais importante.
Portanto, se o if / else parecer melhor, use-o; caso contrário, use uma instrução switch.
fonte
Tópico secundário, mas muitas vezes me preocupo com (e mais frequentemente vejo)
if
/else
e aswitch
declaração ficar muito grande com muitos casos. Isso geralmente prejudica a manutenção.Os culpados comuns incluem:
Consertar:
fonte
De acordo com este link, a comparação IF vs Switch do teste de iteração usando switch e if, é semelhante a 1.000.000.000 de iterações, tempo gasto pela instrução Switch = 43.0s e pela instrução If = 48.0s
Que é literalmente 20833333 iterações por segundo. Portanto, se realmente precisarmos nos concentrar mais,
PS: Apenas para saber a diferença de desempenho para uma pequena lista de condições.
fonte
Se você está apenas usando a instrução if or else, a solução base está usando a comparação? operador
Você pode fazer a rotina ou em um switch
fonte
Na verdade, isso não responde à sua pergunta, mas, como haverá pouca diferença entre as versões compiladas, sugiro que você escreva seu código da maneira que melhor descreva suas intenções. Não apenas existe uma chance melhor do compilador fazer o que você espera, como também facilitará a manutenção de seu código por outras pessoas.
Se sua intenção é ramificar seu programa com base no valor de uma variável / atributo, uma instrução switch representa melhor essa intenção.
Se sua intenção é ramificar seu programa com base em diferentes variáveis / atributos / condições, uma cadeia if / else se representa melhor essa intenção.
Eu concederei que cody está certo sobre as pessoas esquecerem o comando break, mas quase sempre vejo pessoas fazendo blocos complicados se eles errarem o {}, então as linhas que deveriam estar na declaração condicional não. Essa é uma das razões pelas quais sempre incluo {} nas minhas declarações if, mesmo que haja uma linha nela. Não é apenas mais fácil de ler, mas se eu precisar adicionar outra linha na condicional, não posso esquecer de adicioná-la.
fonte
Questão de interesse. Isso surgiu há algumas semanas no trabalho e encontramos uma resposta escrevendo um trecho de exemplo e visualizando-o no .NET Reflector (o refletor é incrível !! eu adoro).
Foi o que descobrimos: Uma instrução switch válida para qualquer coisa que não seja uma string é compilada para IL como uma instrução switch. No entanto, se for uma string, ele será reescrito como um if / else if / else em IL. Portanto, no nosso caso, queríamos saber como as instruções do switch comparam as strings, por exemplo, diferencia maiúsculas de minúsculas etc. e o refletor rapidamente nos deu uma resposta. Isso foi útil para saber.
Se você quiser fazer uma comparação com distinção entre maiúsculas e minúsculas em seqüências de caracteres, poderá usar uma instrução switch, pois é mais rápida do que executar uma String.Compare em um if / else. (Edit: Read O que é mais rápido, ative a string ou o tipo if ?, para alguns testes de desempenho reais) No entanto, se você quiser fazer uma distinção entre maiúsculas e minúsculas, é melhor usar um if / else, pois o código resultante não é bonito.
A melhor regra geral é usar as opções de chave se isso fizer sentido (sério), por exemplo:
Se você precisar manipular o valor para alimentar a instrução switch (crie uma variável temporária para a qual alternar), provavelmente deverá usar uma instrução de controle if / else.
Uma atualização:
Na verdade, é melhor converter a string para maiúscula (por exemplo
ToUpper()
), pois aparentemente houve outras otimizações que o compilador just-in-time pode fazer como quando comparado aoToLower()
. É uma micro otimização, no entanto, em um circuito fechado, pode ser útil.Uma pequena nota lateral:
Para melhorar a legibilidade das instruções de opção, tente o seguinte:
fonte
A declaração switch é definitivamente a mais rápida e a mais se. Existem testes de velocidade fornecidos pelo BlackWasp
http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
--Confira
Mas depende muito das possibilidades que você está tentando explicar, mas tento usar uma instrução switch sempre que possível.
fonte
Não apenas C #, mas todas as linguagens baseadas em C, eu acho: como um switch é limitado a constantes, é possível gerar código muito eficiente usando uma "tabela de salto". O caso C é realmente um bom GOTO computado pela FORTRAN, mas o caso C # ainda é testado contra uma constante.
Não é o caso que o otimizador possa criar o mesmo código. Considere, por exemplo,
Como esses são booleanos compostos, o código gerado precisa calcular um valor e fazer um curto-circuito. Agora considere o equivalente
Isso pode ser compilado em
porque você está dizendo implicitamente ao compilador que ele não precisa calcular os testes OR e igualdade.
fonte
Meu professor de cs sugeriu que você não alternasse as declarações, pois muitas vezes as pessoas esqueciam o intervalo ou o usavam incorretamente. Não consigo lembrar exatamente o que ele disse, mas algo parecido com o fato de que olhar para alguma base de código seminal que mostrava exemplos da declaração de troca (anos atrás) também apresentava muitos erros.
fonte
Algo que acabei de notar é que você pode combinar instruções if / else e switch! Muito útil quando é necessário verificar as pré-condições.
fonte
Eu acho que o switch é mais rápido do que se Condições como ver se existe um programa como:
Escreva um programa para inserir qualquer número (entre 1 - 99) e verifique se está no slot a) 1 - 9 e depois no slot b) 11 - 19 no slot 2 c) 21-29, no slot três e assim por diante até 89- 99
Depois, se você tiver que criar várias condições, mas caso de troca por filho, basta digitar
vai ser tão fácil
Existem muitos outros exemplos também!
fonte
uma instrução switch é basicamente uma comparação para igualdade. O evento do teclado tem uma grande vantagem sobre a instrução switch quando é fácil escrever e ler código, e uma instrução if elseif teria.
Uma instrução if elseif é ótima para mais de uma solução se (theAmountOfApples for maior que 5 && theAmountOfApples for menor que 10) salve suas maçãs se (theAmountOfApples for maior que 10 || theAmountOfApples == 100) venda suas maçãs. Eu não escrevo c # ou c ++, mas eu aprendi antes de aprender java e eles são linguagens próximas.
fonte
Uma possível desvantagem das declarações de switch é a falta de várias condições. Você pode ter várias condições para as instruções if (else), mas não vários casos com condições diferentes em um comutador.
As instruções switch não são adequadas para operações lógicas além do escopo de equações / expressões booleanas simples. Para essas equações / expressões booleanas, é eminentemente adequado, mas não para outras operações lógicas.
Você tem muito mais liberdade com a lógica disponível nas instruções If, mas a legibilidade pode sofrer se a instrução If se tornar difícil de manejar ou for mal administrada.
Ambos têm lugar, dependendo do contexto do que você se depara.
fonte