O avanço da declaração de switch é um dos meus principais motivos pessoais para amar switch
vs. if/else if
construções. Um exemplo está em ordem aqui:
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;
}
As pessoas inteligentes estão se encolhendo porque os string[]
s devem ser declarados fora da função: bem, eles são, isso é apenas um exemplo.
O compilador falha com o seguinte erro:
O controle não pode passar de um rótulo de caso ('caso 3:') para outro O controle não pode passar de um rótulo de caso ('caso 2:') para outro
Por quê? E existe alguma maneira de obter esse tipo de comportamento sem ter três if
segundos?
c#
switch-statement
Matthew Scharley
fonte
fonte
O "porquê" é evitar falhas acidentais, pelas quais sou grato. Esta é uma fonte não incomum de erros em C e Java.
A solução alternativa é usar goto, por exemplo
O design geral de switch / case é um pouco infeliz em minha opinião. Ele ficou muito próximo de C - existem algumas mudanças úteis que podem ser feitas em termos de escopo etc. Indiscutivelmente, uma opção mais inteligente que poderia fazer a correspondência de padrões etc. seria útil, mas isso é realmente mudar da opção para "verificar uma sequência de condições" - nesse ponto, talvez fosse necessário um nome diferente.
fonte
if (result = true) { }
A inovação do switch é historicamente uma das principais fontes de erros nos softwares modernos. O designer do idioma decidiu tornar obrigatório o salto no final do caso, a menos que você esteja padronizando o próximo caso diretamente sem processamento.
fonte
case 1, 2:
nos idiomas que permitem isso. O que nunca entenderei é por que nenhuma linguagem moderna escolheria permitir isso.case 1, 2:
é um rótulo único, mas com vários nomes. - FWIW, acho que a maioria das linguagens proibidas não se aplica ao caso especial do índium "consecutivo case labels", mas trata os rótulos de caso como uma anotação na próxima declaração e exige a última declaração antes de uma declaração rotulada com (um ou mais) etiquetas de caso para ser um salto.Para adicionar as respostas aqui, acho que vale a pena considerar a pergunta oposta em conjunto com isso, viz. por que C permitiu a queda em primeiro lugar?
Qualquer linguagem de programação, é claro, serve a dois objetivos:
A criação de qualquer linguagem de programação é, portanto, um equilíbrio entre a melhor forma de atender a esses dois objetivos. Por um lado, mais fácil é se transformar em instruções de computador (sejam códigos de máquina, códigos de código como IL ou instruções interpretadas na execução), mais capazes de que o processo de compilação ou interpretação seja eficiente, confiável e compacto na saída. Levado ao extremo, esse objetivo resulta em apenas escrever códigos de montagem, IL ou até códigos brutos, porque a compilação mais fácil é onde não há compilação.
Por outro lado, quanto mais a linguagem expressa a intenção do programador, e não os meios adotados para esse fim, mais compreensível é o programa, ao escrever e durante a manutenção.
Agora,
switch
sempre poderia ter sido compilado convertendo-o em uma cadeia equivalente deif-else
blocos ou similar, mas foi projetado para permitir a compilação em um padrão de montagem comum em particular em que se obtém um valor, calcula um deslocamento a partir dele (seja consultando uma tabela indexado por um hash perfeito do valor ou por uma aritmética real no valor *). Vale a pena notar que, atualmente, a compilação de C # às vezes se tornaswitch
equivalenteif-else
e às vezes usa uma abordagem de salto baseado em hash (e da mesma forma com C, C ++ e outras linguagens com sintaxe comparável).Nesse caso, há duas boas razões para permitir falhas:
De qualquer maneira, isso acontece naturalmente: se você criar uma tabela de salto em um conjunto de instruções e um dos lotes anteriores não contiver algum tipo de salto ou retorno, a execução progredirá naturalmente no próximo lote. Permitir falhas era o que "aconteceria" se você transformasse
switch
C em uma tabela de salto - usando código de máquina.Os codificadores que escreveram em assembly já estavam acostumados ao equivalente: ao escrever uma tabela de salto manualmente no assembly, eles teriam que considerar se um determinado bloco de código terminaria com um retorno, um salto fora da tabela ou apenas continuaria. para o próximo bloco. Como tal, fazer com que o codificador adicione um explícito
break
quando necessário também era "natural" para o codificador.Na época, portanto, era uma tentativa razoável de equilibrar os dois objetivos de uma linguagem de computador, pois ela se relaciona ao código de máquina produzido e à expressividade do código-fonte.
Quatro décadas depois, porém, as coisas não são exatamente as mesmas, por algumas razões:
switch
ser transformadaif-else
porque foi considerada a abordagem mais eficiente, ou então transformada em uma variante particularmente esotérica da abordagem de salto em mesa é maior. O mapeamento entre as abordagens de nível superior e inferior não é tão forte quanto era antes.switch
blocos usavam um fall-through além de vários rótulos no mesmo bloco, e pensava-se que o uso- caso aqui significava que esses 3% eram de fato muito mais altos que o normal). Portanto, a linguagem estudada torna o incomum mais facilmente atendido do que o comum.Em relação aos dois últimos pontos, considere a seguinte citação da edição atual da K&R:
Portanto, pela boca do cavalo, a queda em C é problemática. É uma boa prática sempre documentar falhas com comentários, que é uma aplicação do princípio geral de que se deve documentar onde se faz algo incomum, porque é isso que fará o exame posterior do código e / ou fará com que seu código pareça tem um bug de novato quando está de fato correto.
E quando você pensa sobre isso, codifique assim:
Está adicionando algo para tornar explícito o detalhamento no código, simplesmente não é algo que pode ser detectado (ou cuja ausência pode ser detectada) pelo compilador.
Como tal, o fato de que on tem que ser explícito com fall-through em C # não adiciona nenhuma penalidade às pessoas que escreveram bem em outras linguagens de estilo C de qualquer maneira, pois elas já seriam explícitas em seus fall-throughs. †
Finalmente, o uso
goto
aqui já é uma norma do C e de outras linguagens:Nesse tipo de caso em que queremos que um bloco seja incluído no código executado para um valor diferente daquele que leva um ao bloco anterior, já estamos tendo que usá-lo
goto
. (Obviamente, existem meios e maneiras de evitar isso com condicionais diferentes, mas isso é verdade em quase tudo relacionado a essa pergunta). Como tal, o C # se baseou na maneira já normal de lidar com uma situação em que queremos obter mais de um bloco de código em umswitch
e apenas generalizá-lo para cobrir falhas. Isso também tornou os dois casos mais convenientes e com autodocumentação, pois precisamos adicionar um novo rótulo em C, mas podemos usá-locase
como um rótulo em C #. Em C #, podemos nos livrar dobelow_six
rótulo e usar ogoto case 5
que é mais claro quanto ao que estamos fazendo. (Também teríamos que adicionarbreak
para odefault
, que deixei de fora apenas para tornar o código C acima claramente não o código C #).Em resumo, portanto:
break
, para facilitar o aprendizado do idioma por pessoas familiarizadas com idiomas semelhantes e facilitar a portabilidade.goto
abordagem para atingir o mesmo bloco decase
rótulos diferentes que o usado no C. Ele apenas o generaliza em alguns outros casos.goto
abordagem baseada em mais conveniente e mais clara do que em C, permitindo que ascase
instruções funcionem como rótulos.Em suma, uma decisão de design bastante razoável
* Algumas formas de BASIC permitiriam fazer coisas
GOTO (x AND 7) * 50 + 240
que, embora frágeis e, portanto, um caso particularmente convincente de proibiçãogoto
, servem para mostrar um equivalente em idioma mais alto do tipo de maneira como o código de nível inferior pode dar um salto com base em aritmética sobre um valor, que é muito mais razoável quando é o resultado da compilação, em vez de algo que deve ser mantido manualmente. As implementações do Duff's Device, em particular, se prestam bem ao código de máquina ou IL equivalente, porque cada bloco de instruções geralmente terá o mesmo comprimento sem a necessidade de adição denop
preenchimentos.† O dispositivo de Duff aparece aqui novamente, como uma exceção razoável. O fato de que, com esses padrões e similares, há uma repetição de operações, serve para tornar o uso do fall-through relativamente claro, mesmo sem um comentário explícito nesse sentido.
fonte
Você pode 'ir para o rótulo da caixa' http://www.blackwasp.co.uk/CSharpGoto.aspx
fonte
Eles deixaram de fora esse comportamento por design para evitar quando não era usado pela vontade, mas causava problemas.
Pode ser usado apenas se não houver declaração na parte do caso, como:
fonte
Eles mudaram o comportamento da instrução switch (de C / Java / C ++) para c #. Eu acho que o raciocínio foi que as pessoas esqueceram a queda e erros foram causados. Um livro que li disse usar goto para simular, mas isso não parece uma boa solução para mim.
fonte
goto case
é para isso. Sua vantagem sobre o avanço é que é explícito. O fato de algumas pessoas aqui se oporemgoto case
apenas a mostrar que foram doutrinadas contra "ir" sem entender o assunto e são incapazes de pensar por conta própria. Quando Dijkstra escreveu "GOTO Considerado Prejudicial", ele estava abordando idiomas que não tinham outros meios de alterar o fluxo de controle.goto
pode ser na otimização de código?Você pode obter uma queda como c ++ pela palavra-chave goto.
EX:
fonte
- documentação do switch C # ()
fonte
Após cada instrução de caso, exija instrução break ou goto , mesmo que seja um caso padrão.
fonte
Apenas uma observação rápida para acrescentar que o compilador para o Xamarin realmente entendeu errado e permite o avanço. Supostamente foi corrigido, mas não foi lançado. Descobri isso em algum código que realmente estava caindo e o compilador não reclamou.
fonte
switch (Referência de C #) diz
Portanto, você também precisará adicionar um
break;
à suadefault
seção, caso contrário, ainda haverá um erro do compilador.fonte
O C # não suporta falhas com instruções de opção / caso. Não sei por que, mas realmente não há suporte para isso. Ligação
fonte
goto case
. Essa foi uma escolha intencional e inteligente de design pelos designers de C #.Você esqueceu de adicionar o "intervalo"; instrução no caso 3. No caso 2, você o escreveu no bloco if. Portanto, tente o seguinte:
fonte