Pensei sobre isso e não consegui dar um exemplo. Por que alguém iria querer pegar uma exceção e não fazer nada a respeito? Você pode dar um exemplo? Talvez seja apenas algo que nunca deve ser feito.
Pensei sobre isso e não consegui dar um exemplo. Por que alguém iria querer pegar uma exceção e não fazer nada a respeito? Você pode dar um exemplo? Talvez seja apenas algo que nunca deve ser feito.
Respostas:
Eu faço isso o tempo todo com coisas como erros de conversão em D:
import std.conv, std.stdio, std.exception;
void main(string[] args) {
enforce(args.length > 1, "Usage: foo.exe filename");
double[] nums;
// Process a text file with one number per line into an array of doubles,
// ignoring any malformed lines.
foreach(line; File(args[1]).byLine()) {
try {
nums ~= to!double(line);
} catch(ConvError) {
// Ignore malformed lines.
}
}
// Do stuff with nums.
}
Dito isso, acho que todos os blocos de captura devem ter algo neles, mesmo que seja apenas um comentário explicando por que você está ignorando a exceção.
Editar: também quero enfatizar que, se você fizer algo assim, tenha cuidado para capturar apenas a exceção específica que deseja ignorar. Fazer um velho simples catch {}
é quase sempre uma coisa ruim.
Um exemplo em que eu acho que é bom engolir exceção sem fazer nada, mesmo registrando a exceção, está dentro do próprio código de log.
Se você tentou registrar algo e recebeu uma exceção, não há muito o que fazer:
Isso deixa você com a opção "menos perigosa" de engoli-lo silenciosamente, permitindo que o aplicativo continue executando seu trabalho principal, mas comprometendo a capacidade de solucioná-lo.
Se uma exceção for lançada por uma operação que fosse opcional de qualquer maneira, talvez não haja nada a fazer. Pode não ser útil registrá-lo. Nesse caso, você pode apenas querer pegá-lo e não fazer nada.
Se você estiver fazendo isso, inclua um comentário. Sempre comente qualquer coisa que pareça um bug (descoberta em uma switch
instrução, catch
bloco vazio , atribuição em uma condição).
Você pode argumentar que esse não é um uso adequado de exceções, mas isso é para outra questão.
catch
Blocos vazios são um cheiro de código na maioria dos idiomas. A idéia principal é usar exceções para situações excepcionais e não usá-las para controle lógico. Todas as exceções devem ser tratadas em algum lugar.
Como conseqüência da adoção dessa abordagem, ao programar uma camada de aplicativo específica, você tem várias opções:
Isso realmente não deixa espaço para catch
blocos vazios e sem nada .
EDITADO PARA ADICIONAR : suponha que você esteja programando em uma linguagem onde lançar exceções é a maneira normal de controlar a lógica do programa (uma das alternativas a goto
). Então, um catch
bloco vazio em um programa escrito nesse idioma é muito parecido com um else
bloco vazio em um idioma tradicional (ou nenhum else
bloco). No entanto, acredito que esse não é o estilo de programação recomendado pelas comunidades de desenvolvimento em C #, Java ou C ++.
Em alguns casos, o Java exige que você lide com uma exceção que, sob nenhuma circunstância, pode acontecer. Considere este código:
try {
bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}
Toda implementação da plataforma Java é necessária para suportar os seguintes charsets padrão.
...
UTF-8
Sem quebrar sua plataforma Java, simplesmente não há como causar essa exceção. Então, por que se preocupar em lidar com isso? Mas você não pode simplesmente omitir a cláusula try-catch-catch.
throw new Error(e)
apenas para ter certeza absoluta de que não perdi nada (ou relatei uma plataforma realmente quebrada).
Como regra geral, não. Você quer lançar a exceção na pilha ou registrar o que aconteceu.
No entanto, costumo fazer isso quando estou analisando cadeias de caracteres e convertendo-as em números (especialmente em projetos de mapeamento que são da variedade de uso único e deitar fora).
boolean isNonZeroNumber = false;
try {
if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
b = true;
}
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}
return b;
Em alguns casos, a exceção não é de todo excepcional; de fato, é esperado. Considere este exemplo:
/* Check if the process is still alive; exitValue() throws an exception if it is */
try {
p.exitValue();
processPool.remove(p);
}
catch (IllegalThreadStateException e) { /* expected behaviour */ }
Como não há método isAlive () em java.lang.Process (), a única maneira de verificar se ele ainda está ativo é chamar exitValue (), que gera uma IllegalThreadStateException se o processo ainda estiver em execução.
Na minha humilde experiência, raramente isso é bom. Sua milhagem pode variar.
Quase todas as vezes que eu vi isso em nossa base de código, foi porque rastreei um bug que a exceção consumida ocultou. Em outras palavras, ao não fornecer nenhum código, o aplicativo não tem como indicar um erro ou executar outra ação.
Às vezes, nas camadas da API, por exemplo, é possível que você não queira ou não permita que as exceções passem. Nesse caso, eu tento tentar pegar o mais genérico e registrá-lo. Mais uma vez, para pelo menos dar uma chance a uma sessão de depuração / diagnóstico.
Normalmente, se precisar estar vazio, o mínimo que faço é colocar algum tipo de afirmação; portanto, pelo menos algo acontece durante uma sessão de depuração. Dito isto, se eu não posso lidar com isso, tendem a omiti-los completamente.
Na IMO, há um lugar em que as declarações de captura vazias estão OK. Nos casos de teste em que você espera uma exceção:
try
{
DoSomething(); //This should throw exception if everything works well
Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
//Expected to get here
}
Acredito que há certos casos em que se justifica ter uma cláusula catch vazia - esses são casos em que você deseja que o código continue se uma operação específica falhar. No entanto, acho que esse padrão pode ser facilmente usado demais, portanto, cada uso deve ser examinado de perto para garantir que não haja uma maneira melhor de lidar com isso. Em particular, isso nunca deve ser feito se deixar o programa em um estado inconsistente ou se puder levar à falha de alguma outra parte do código posteriormente.
Não acredito em não ter algum nível de alerta quando ocorre uma exceção - um dos objetivos da programação não deveria ser o de melhorar o código? Se uma exceção ocorrer 10% do tempo e você nunca souber disso, a exceção sempre será ruim ou pior.
No entanto, vejo o outro lado, que se a exceção não causa nenhum dano real no processamento do código, por que expor o usuário a ela. A solução que eu normalmente implico é registrá-la pela minha classe de logger (que está em todas as classes que eu já escrevo no trabalho).
Se for uma exceção benigna, faço uma chamada para o método .Debug () do meu Logger; caso contrário, Logger.Error () (e (talvez) jogue).
try
{
doWork();
}
catch(BenignException x)
{
_log.Debug("Error doing work: " + x.Message);
}
A propósito, na minha implementação do meu logger, fazer uma chamada para o método .Debug () só o registrará se o meu arquivo App.Config especificar que o nível do log de execução do programa está no modo de Depuração.
A verificação de existência é um bom caso de uso:
// If an exception happens, it doesn't matter. Log the initial value or the new value
function logId(person) {
let id = 'No ID';
try {
id = person.data.id;
} catch {}
console.log(id);
}
Outro caso é quando uma finally
cláusula é usada:
function getter(obj){}
function objChecker()
{
try
{
/* Check argument length and constructor type */
if (getter.length === 1 && getter.constructor === Function)
{
console.log("Correct implementation");
return true;
}
else
{
console.log("Wrong implementation");
return false;
}
}
catch(e)
{
}
finally
{
console.log(JSON.stringify(getter))
}
}
// Test the override of the getter method
getter = getter;
objChecker();
getter = [];
objChecker();
getter = {};
objChecker();
getter = RegExp;
objChecker();
getter = Function;
objChecker();
Existe uma proposta para permitir a omissão da ligação de captura no ECMAScript que se refere a este e a casos de uso semelhantes.
Referências
A única vez em que realmente precisei fazer algo assim foi com uma classe de log para um aplicativo de console. Havia um manipulador de captura global de nível superior que emitiu o erro para STDOUT, registrou o erro em um arquivo e fechou o aplicativo com um código de erro> 0.
O problema aqui era que, às vezes, o log do arquivo falhava. As permissões de diretório impediram você de gravar o arquivo, o arquivo foi bloqueado exclusivamente, o que seja. Se isso acontecesse, eu não poderia lidar com a exceção da mesma maneira que havia em outro lugar (capturar, registrar os detalhes relevantes , agrupar um tipo de exceção melhor etc.) porque o registro dos detalhes da exceção fazia com que o criador de logs passasse pelas mesmas ações ( tentando gravar em um arquivo) que já havia falhado. Quando eles falharam novamente ... Você vê para onde estou indo. Ele entra em um loop infinito.
Se você soltar alguns dos blocos try {}, poderá acabar com a exceção exibida em uma caixa de mensagem (não o que você deseja para um aplicativo de configuração silenciosa). Portanto, nesse método que grava no arquivo de log, eu tenho um manipulador de captura vazio. Isso não oculta o erro, pois você ainda recebe o código de erro> 0, apenas interrompe o loop infinito e permite que o aplicativo saia normalmente.
Há um comentário, é claro, explicando por que está vazio.
(Eu ia postar isso antes, só tinha medo de ser queimado no esquecimento. Já que eu não sou o único ...)
Tudo bem na situação em que alguma sequência deve ser executada, independentemente da peça mais crítica colocada no final.
Por exemplo. Em comunicação de controle de movimento do host para o controlador. Em situações de emergência, existem várias etapas de preparação para interromper o movimento, interromper a geração de trajetória, desacelerar motores, ativar interrupções, desativar o amplificador e, depois disso, você deve matar a energia do barramento. Tudo deve acontecer sequencialmente e, se uma das etapas falhar, as demais devem ser executadas de qualquer maneira, mesmo com o risco aceito de danificar o hardware. Digamos, mesmo que tudo acima falhe, a energia do barramento de interrupção deve acontecer de qualquer maneira.
Fechando os recursos do sistema normalmente para se recuperar de um erro
Por exemplo. Se você estiver executando um serviço no fundo que não fornece feedback para o usuário, você pode não querer empurrar uma indicação do erro para o usuário, mas você não precisa fechar os recursos que estão sendo utilizados de maneira graciosa.
try
{
// execution code here
}
catch(Exception e)
{
// do nothing here
}
finally
{
// close db connection
// close file io resource
// close memory stream
// etc...
}
Idealmente, você usaria a captura no modo de depuração para capturar erros e imprimi-los no console ou em um arquivo de log para teste. Em um ambiente de produção, geralmente espera-se que os serviços em segundo plano não interrompam o usuário quando um erro é gerado, portanto a saída do erro deve ser suprimida.