Em uma das minhas primeiras revisões de código (um tempo atrás), me disseram que é uma boa prática incluir uma cláusula padrão em todas as instruções de opção. Recentemente, lembrei-me deste conselho, mas não me lembro qual era a justificativa. Parece bastante estranho para mim agora.
Existe uma razão sensata para sempre incluir uma declaração padrão?
Este idioma depende? Não me lembro qual idioma eu estava usando na época - talvez isso se aplique a alguns idiomas e não a outros?
default
switch-statement
tttppp
fonte
fonte
Respostas:
Os casos de troca quase sempre devem ter um
default
caso.Razões para usar um
default
1.Para 'pegar' um valor inesperado
2. Para lidar com ações 'padrão', onde os casos são para comportamento especial.
Você vê muito isso em programas orientados a menus e em scripts do shell bash. Você também pode ver isso quando uma variável é declarada fora da caixa de seleção, mas não inicializada, e cada caso a inicializa para algo diferente. Aqui, o padrão também precisa inicializá-lo para que o código de linha que acessa a variável não apresente erro.
3. Para mostrar a alguém lendo seu código que você cobriu esse caso.
Este foi um exemplo simplificado demais, mas o ponto é que alguém que lê o código não deve se perguntar por
variable
que não pode ser algo diferente de 1 ou 2.O único caso em que posso pensar em NÃO usar
default
é quando o switch está verificando algo em que é óbvio que todas as outras alternativas podem ser ignoradas com alegriafonte
enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;
cria umaMyEnum
instância válida que não é igual aFOO
ouBAR
. Se um desses problemas causar um erro no seu projeto, um caso padrão o ajudará a encontrar o erro muito rapidamente, geralmente sem a necessidade de depurador!Não.
E se não houver ação padrão, o contexto é importante. E se você quiser apenas agir com alguns valores?
Veja o exemplo da leitura de teclas pressionadas para um jogo
Adicionando:
É apenas uma perda de tempo e aumenta a complexidade do código sem motivo.
fonte
// do nothing
comentário deixa claro que está 'ok' se nem todos os casos forem cobertos, em oposição a outras instruções de opção em que isso seria 'não está ok'.NÃO ter o caso padrão pode realmente ser benéfico em algumas situações.
Se seus casos de opção forem valores de enumeração, por não ter um caso padrão, você poderá receber um aviso do compilador se estiver faltando algum caso. Dessa forma, se novos valores de enumeração forem adicionados no futuro e você esquecer de adicionar casos para esses valores na opção, poderá descobrir o problema no momento da compilação. Você ainda deve garantir que o código execute a ação apropriada para valores não manipulados, caso um valor inválido tenha sido convertido para o tipo de enumeração. Portanto, isso pode funcionar melhor em casos simples em que você pode retornar dentro do caso enum em vez de interromper.
fonte
Eu sempre usaria uma cláusula padrão, independentemente do idioma em que você está trabalhando.
As coisas podem dar errado. Valores não serão o que você espera, e assim por diante.
Não querer incluir uma cláusula padrão implica que você está confiante de que conhece o conjunto de valores possíveis. Se você acredita que conhece o conjunto de valores possíveis, se o valor estiver fora desse conjunto de valores possíveis, convém ser informado - é certamente um erro.
Essa é a razão pela qual você deve sempre usar uma cláusula padrão e gerar um erro, por exemplo, em Java:
Não há razão para incluir mais informações do que apenas a string "inacessível"; se isso realmente acontecer, você precisará examinar a origem e os valores das variáveis, etc. de qualquer maneira, eo rastreamento de pilha de exceção incluirá esse número de linha, portanto, não será necessário perder tempo escrevendo mais texto na mensagem de exceção.
fonte
throw new RuntimeException("myVar invalid " + myVar);
pois isso pode fornecer informações suficientes para descobrir e corrigir o erro sem a necessidade de usar um depurador e inspecionar variáveis, o que pode ser difícil se esse for um problema que raramente ocorre e é difícil de reproduzir.default:
. Porém, mais frequentemente, não se observa um padrão, porque se pensamyVar
que nunca poderá ter outro valor além dos listados; no entanto, não posso contar o número de vezes que fiquei surpreso na vida real quando a variável teve um valor diferente dos valores que "poderia" ter. Nesses casos, sou grato pela exceção, fazendo-me ver isso imediatamente, em vez de causar outro erro mais tarde (mais difícil de depurar) ou resposta errada (pode ser ignorada nos testes).AssertionError
vez deRuntimeException
, pois essa situação é muito semelhante a uma afirmação afirmando quemyVar
é um dos valores manipulados. Também a chance de alguém pegar e engolir o erro é menor.Na minha empresa, escrevemos software para o mercado de aviônicos e de defesa e sempre incluímos uma declaração padrão, porque TODOS os casos em uma declaração de chave devem ser explicitamente tratados (mesmo que seja apenas um comentário dizendo 'Não faça nada'). Não podemos pagar pelo software apenas para se comportar mal ou simplesmente travar com valores inesperados (ou mesmo o que pensamos impossíveis).
Pode-se discutir que um caso padrão nem sempre é necessário, mas, ao exigir sempre, é facilmente verificado pelos nossos analisadores de código.
fonte
Uma instrução "switch" deve sempre incluir uma cláusula padrão? Não. Deve normalmente incluem um padrão.
A inclusão de uma cláusula padrão só faz sentido se houver algo a fazer, como afirmar uma condição de erro ou fornecer um comportamento padrão. Incluir um "apenas porque" é uma programação de cultos de carga e não oferece valor. É o equivalente "switch" de dizer que todas as declarações "if" devem incluir um "else".
Aqui está um exemplo trivial de onde não faz sentido:
Isso é equivalente a:
fonte
default:
nesses casos codifica suas suposições. Por exemplo, não há problema emsgn==0
imprimirinteger
(nem positivo nem negativo), ou isso é um erro? Para mim, lendo esse código, é difícil dizer. Suponho que você não queira escreverzero
nesse casointeger
e que o programador assumiu quesgn
só pode ser -1 ou +1. Se fosse esse o caso,default:
permitir que o programador capturasse o erro de suposição mais cedo e alterasse o código.Tanto quanto eu vejo, a resposta é 'padrão' é opcional, dizer que um comutador deve sempre conter um padrão é como dizer que todo 'if-elseif' deve conter um 'else'. Se houver uma lógica a ser executada por padrão, a instrução 'default' deverá estar lá, mas, caso contrário, o código poderá continuar em execução sem fazer nada.
fonte
Ter uma cláusula padrão quando não é realmente necessário é programação defensiva Isso geralmente leva a códigos excessivamente complexos por causa de muito código de manipulação de erros. Esse código de manipulação e detecção de erros prejudica a legibilidade do código, dificulta a manutenção e, eventualmente, leva a mais erros do que resolve.
Portanto, acredito que se o padrão não for atingido - você não precisará adicioná-lo.
Observe que "não deve ser alcançado" significa que, se atingido, é um erro no software - você precisa testar valores que podem conter valores indesejados devido à entrada do usuário, etc.
fonte
Eu diria que depende do idioma, mas em C, se você estiver ativando um tipo de enum e manipular todos os valores possíveis, provavelmente será melhor NÃO incluir um caso padrão. Dessa forma, se você adicionar uma tag enum adicional posteriormente e esquecer de adicioná-la ao comutador, um compilador competente emitirá um aviso sobre o caso ausente.
fonte
gcc -Wall
(tipo do menor denominador comum de compiladores competentes) emite avisos sobre enumerações não tratadas nas instruções do switch.Se você souber que a instrução switch só terá um conjunto estritamente definido de rótulos ou valores, faça isso para cobrir as bases, para obter sempre resultados válidos. Basta colocar o padrão sobre o rótulo que programaticamente / logicamente ser o melhor manipulador para outros valores.
fonte
default
ainda é necessário aqui? Ou isso permite alguma sintaxe especial que lhe permitiu omitir?Pelo menos não é obrigatório em Java. De acordo com a JLS, diz que pelo menos um caso padrão pode estar presente. O que significa que nenhum caso padrão é aceitável. Às vezes, também depende do contexto em que você está usando a instrução switch. Por exemplo, em Java, o seguinte bloco de opções não requer maiúsculas e minúsculas padrão
Mas no método a seguir, que espera retornar uma String, o caso padrão é útil para evitar erros de compilação
embora você possa evitar erros de compilação para o método acima sem ter uma caixa padrão, basta ter uma declaração de retorno no final, mas fornecer uma caixa padrão torna mais legível.
fonte
Algumas diretrizes (desatualizadas) dizem isso, como MISRA C :
Esse conselho está desatualizado porque não se baseia nos critérios atualmente relevantes. A omissão flagrante é o que Harlan Kassler disse:
Deixar de fora o caso padrão permite que o compilador avise ou falhe opcionalmente quando vir um caso não tratado. Afinal, a verificação estática é melhor do que qualquer verificação dinâmica e, portanto, não é um sacrifício digno para quando você também precisa da verificação dinâmica.
Como Harlan também demonstrou, o equivalente funcional de um caso padrão pode ser recriado após a troca. O que é trivial quando cada caso é um retorno antecipado.
A necessidade típica de uma verificação dinâmica é a manipulação de entrada, em um sentido amplo. Se um valor vier de fora do controle do programa, ele não será confiável.
É também aqui que Misra assume o ponto de vista da programação defensiva extrema, pela qual, desde que um valor inválido seja fisicamente representável, ele deve ser verificado, independentemente de o programa estar comprovadamente correto. O que faz sentido se o software precisar ser o mais confiável possível na presença de erros de hardware. Mas como Ophir Yoktan disse, a maioria dos softwares é melhor não "manipular" bugs. A última prática é às vezes chamada de programação ofensiva .
fonte
Você deve ter um padrão para capturar valores não esperados chegando.
No entanto, discordo de Adrian Smith de que sua mensagem de erro por padrão deve ser algo totalmente sem sentido. Pode haver um caso não tratado que você não viu (o que é o ponto) que o usuário acabará vendo e uma mensagem como "inacessível" é totalmente inútil e não ajuda ninguém nessa situação.
Caso em questão, quantas vezes você teve um BSOD totalmente sem sentido? Ou uma exceção fatal @ 0x352FBB3C32342?
fonte
Se o valor da chave ( chave (variável )) não atingir o caso padrão, o caso padrão não será necessário. Mesmo se mantivermos o caso padrão, ele não será executado. É um código morto.
fonte
É uma 'convenção' de codificação opcional. Dependendo do uso, é necessário ou não. Pessoalmente, acredito que se você não precisar, não deverá estar lá. Por que incluir algo que não será usado ou alcançado pelo usuário?
Se as possibilidades de maiúsculas e minúsculas são limitadas (ou seja, um booleano), a cláusula padrão é redundante !
fonte
Se não houver um caso padrão em uma
switch
instrução, o comportamento poderá ser imprevisível se esse caso surgir em algum momento, o que não era previsível no estágio de desenvolvimento. É uma boa prática incluir umdefault
caso.Tal prática pode resultar em um erro como desreferência NULL , vazamento de memória e outros tipos de erros graves .
Por exemplo, assumimos que cada condição inicializa um ponteiro. Mas se o
default
caso deve surgir e se não inicializarmos nesse caso, há toda a possibilidade de aparecer uma exceção de ponteiro nulo. Portanto, é sugerido o uso de umadefault
declaração de caso, mesmo que possa ser trivial.fonte
O caso padrão pode não ser necessário no comutador usado pelo enum. Quando switch contiver todo o valor, o caso padrão nunca será executado. Portanto, neste caso, não é necessário.
fonte
Depende de como o switch em um determinado idioma funciona, no entanto, na maioria dos idiomas, quando nenhum caso é correspondido, a execução é executada na instrução switch sem aviso. Imagine que você esperou algum conjunto de valores e os manipulou no switch, mas obtém outro valor na entrada. Nada acontece e você não sabe que nada aconteceu. Se você pegasse o caso por padrão, saberia que havia algo errado.
fonte
Discordo da resposta mais votada de Vanwaril acima.
Qualquer código adiciona complexidade. Também testes e documentação devem ser feitos para isso. Portanto, é sempre bom se você pode programar usando menos código. Minha opinião é que eu uso uma cláusula padrão para declarações de chave não exaustivas, enquanto eu não uso cláusula padrão para declarações de chave exaustivas. Para ter certeza de que fiz direito, use uma ferramenta de análise de código estática. Então, vamos entrar nos detalhes:
Instruções de chave não exaustivas: essas devem sempre ter um valor padrão. Como o nome sugere, são declarações que não cobrem todos os valores possíveis. Isso também pode não ser possível, por exemplo, uma instrução switch em um valor inteiro ou em uma String. Aqui eu gostaria de usar o exemplo de Vanwaril (deve-se mencionar que acho que ele usou esse exemplo para fazer uma sugestão errada. Eu o uso aqui para indicar o oposto -> Usar uma declaração padrão):
O jogador pode pressionar qualquer outra tecla. Então não poderíamos fazer nada (isso pode ser mostrado no código apenas adicionando um comentário ao caso padrão) ou, por exemplo, deve imprimir algo na tela. Este caso é relevante, pois pode acontecer.
Instruções de chave exaustivas: Essas instruções de chave cobrem todos os valores possíveis, por exemplo, uma declaração de chave em uma enumeração de tipos de sistema de notas. Ao desenvolver código pela primeira vez, é fácil cobrir todos os valores. No entanto, como somos humanos, há uma pequena chance de esquecer alguns. Além disso, se você adicionar um valor de enum posteriormente, de modo que todas as instruções de opção tenham que ser adaptadas para torná-las exaustivas novamente, abrirá o caminho para o erro. A solução simples é uma ferramenta de análise de código estática. A ferramenta deve verificar todas as instruções de opção e verificar se são exaustivas ou se têm um valor padrão. Aqui está um exemplo para uma declaração exaustiva de chave. Primeiro, precisamos de um enum:
Então precisamos de uma variável desse enum como
GradeSystemType type = ...
. Uma declaração de switch exaustiva seria assim:Portanto, se estendermos o
GradeSystemType
by, por exemplo,System1To3
a ferramenta de análise de código estático deve detectar que não há cláusula padrão e a instrução switch não é exaustiva, portanto, estamos salvos.Apenas mais uma coisa. Se sempre usarmos uma
default
cláusula, pode acontecer que a ferramenta de análise de código estático não seja capaz de detectar instruções de chave exaustivas ou não exaustivas, pois sempre detecta adefault
cláusula. Isso é muito ruim, pois não seremos informados se estendermos o enum por outro valor e esquecermos de adicioná-lo a uma instrução switch.fonte
As instruções switch devem sempre conter uma cláusula padrão? Nenhum caso de comutação pode existir sem o caso padrão; no caso de comutador, o caso padrão acionará o valor do comutador
switch(x)
neste caso x quando não corresponder a nenhum outro valor de caso.fonte
Acredito que isso seja bastante específico da linguagem e, para o caso C ++, um ponto menor para o tipo de classe enum . O que parece mais seguro que o C enum tradicional. MAS
Se você observar a implementação do std :: byte, é algo como:
Fonte: https://en.cppreference.com/w/cpp/language/enum
E considere também isso:
Fonte: https://en.cppreference.com/w/cpp/language/list_initialization
Este é um exemplo de classe enum que representa valores que não são enumeradores definidos. Por esse motivo, você não pode confiar totalmente em enumerações. Dependendo da aplicação, isso pode ser importante.
No entanto, eu realmente gosto do que @Harlan Kassler disse em seu post e vou começar a usar essa estratégia em algumas situações.
Apenas um exemplo de classe enum não segura:
fonte