Quando é apropriado usar uma instrução de chave fall-through (clássica)? Esse uso é recomendado e incentivado ou deve ser evitado a todo custo?
Quando é apropriado usar uma instrução de chave fall-through (clássica)? Esse uso é recomendado e incentivado ou deve ser evitado a todo custo?
Respostas:
Aqui está um exemplo em que isso seria útil.
public Collection<LogItems> GetAllLogItems(Level level) {
Collection<LogItems> result = new Collection<LogItems>();
switch (level) {
// Note: fall through here is INTENTIONAL
case All:
case Info:
result.Add(GetItemsForLevel(Info));
case Warning:
result.Add(GetItemsForLevel(Warning));
case Error:
result.Add(GetItemsForLevel(Error));
case Critical:
result.Add(GetItemsForLevel(Critical));
case None:
}
return result;
}
Acho que esse tipo de coisa (onde um caso inclui o outro) é bastante raro, e é por isso que algumas linguagens mais recentes não permitem fallover ou exigem uma sintaxe especial.
// INTENTIONAL FALL THROUGH HERE
comentário obrigatório de maiúsculas .
GetItemsForLevel(Info)
chamada GetItemsForLevel(Warning)
e assim por diante.
Eu os uso quando certas funcionalidades precisam ser aplicadas para mais de um valor. Por exemplo, digamos que você tenha um objeto com uma propriedade chamada operationCode. Se o código for igual a 1, 2, 3 ou 4, você deseja iniciarOperaçãoX (). Se for 5 ou 6, você deseja startOperationY () e 7 você startOperationZ (). Por que ter 7 casos completos com funcionalidade e quebras quando você pode usar fall-throughs?
Eu acho que é completamente válido em certas situações, especialmente se evitar 100 declarações if-else. =)
switch
com vários case
s vinculados ao mesmo código. Isso é diferente de uma falha, onde a execução de uma case
continua na próxima por causa da falta de uma break
entre elas.
Casos de queda são perfeitamente adequados. Costumo achar que uma enumeração é usada em muitos lugares e que, quando você não precisa diferenciar alguns casos, é mais fácil usar a lógica fall-through.
Por exemplo (observe os comentários explicativos):
public boolean isAvailable(Server server, HealthStatus health) {
switch(health) {
// Equivalent positive cases
case HEALTHY:
case UNDER_LOAD:
return true;
// Equivalent negative cases
case FAULT_REPORTED:
case UNKNOWN:
case CANNOT_COMMUNICATE:
return false;
// Unknown enumeration!
default:
LOG.warn("Unknown enumeration " + health);
return false;
}
}
Acho esse tipo de uso perfeitamente aceitável.
case X: case Y: doSomething()
e case X: doSomething(); case Y: doAnotherThing()
. No primeiro, a intenção é bastante explícita, enquanto no segundo, a queda pode ser intencional ou não. Na minha experiência, o primeiro exemplo nunca dispara nenhum aviso nos compiladores / analisadores de código, enquanto o segundo exemplo. Pessoalmente, eu chamaria apenas o segundo exemplo de "falha" - embora não tenha certeza de uma definição "oficial".
Isso depende de:
Os dois principais problemas associados a deixar um caso passar para o próximo são:
Isso torna seu código dependente da ordem das instruções de caso. Não é esse o caso, se você nunca falha, e acrescenta um grau de complexidade que geralmente não é bem-vindo.
Não é óbvio que o código para um caso inclua o código para um ou mais casos subsequentes.
Alguns lugares proíbem explicitamente a queda. Se você não trabalha em um lugar desses e se sente confortável com a prática, e se a quebra do código em questão não causar nenhum sofrimento real, talvez não seja a pior coisa do mundo. Se você fizer isso, certifique-se de colocar um comentário chamativo nas proximidades para avisar aqueles que virão mais tarde (incluindo o futuro que você).
Aqui está um exemplo rápido (reconhecidamente incompleto (sem manipulação especial do ano bissexto)) de queda, tornando minha vida mais simples:
function get_julian_day (date) {
int utc_date = date.getUTCDate();
int utc_month = date.getUTCMonth();
int julian_day = 0;
switch (utc_month) {
case 11: julian_day += 30;
case 10: julian_day += 31;
case 9: julian_day += 30;
case 8: julian_day += 31;
case 7: julian_day += 31;
case 6: julian_day += 30;
case 5: julian_day += 31;
case 4: julian_day += 30;
case 3: julian_day += 31;
case 2: julian_day += 28;
case 1: julian_day += 31;
default: break;
}
return julian_day + utc_date;
}
Se sinto necessidade de ir de um caso para outro (raro, é verdade), prefiro ser muito explícito e goto case
, é claro, que assume que seu idioma é compatível.
Como o detalhamento é tão incomum e muito fácil de ignorar durante a leitura do código, acho apropriado ser explícito - e um passo, mesmo que seja um caso, deve se destacar como um dedo dolorido.
Também ajuda a evitar erros que podem ocorrer quando as instruções de caso são reordenadas.