É sempre bom ter uma declaração de captura vazia?


58

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.


E finalmente?
Chris

2
btw - isso foi solicitado no SO no ano passado: stackoverflow.com/questions/1234343/…
warren

17
deve conter pelo menos um comentário por que está vazio.
Peterchen

Respostas:


77

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.


15
+1 para comentário de explicação.
Michael K

Alguns IDEs permitem que você especifique que um nome específico para a exceção (geralmente "ignorar") indica que você a está propositalmente ignorando, o que suprime o aviso que seria exibido; isso substitui o comentário.
precisa

Não conheço D, mas presumo que seja possível fazer algo como o TryParse booleano (linha de seqüência de caracteres, valToParse duplo), que pode ser mais limpo.
perfil completo de ysolik

4
Uau, alguém que realmente usa D! :-P
James McNellis

4
Como o @Michael diz - não está completamente vazio, já que você tem o comentário lá; deve ser obrigatório adicionar um comentário "essa captura foi deixada em branco intencionalmente" se não houver uma declaração
STW

50

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:

  • você não pode logar, é claro;
  • você poderá voltar a um mecanismo de registro de reserva, mas a maioria dos aplicativos não é tão sofisticada e, para aqueles que são sofisticados o suficiente, o mesmo problema de design surgirá com o mecanismo de registro de reserva;
  • falha rápida - será uma solução muito radical para a maioria dos aplicativos;
  • lançar exceção fará pouco bem, pois acabará em uma instrução catch na pilha que quase certamente tentará registrá-la ...

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.


10
ótimo ponto. depurar o código de depuração é sempre um desafio.
DevSolo #

7
Isso, um milhão de vezes. Especialmente quando você considera que, se estiver registrando, pode estar em um estado terrível; você pode estar sem memória, pode estar falhando, pode ser qualquer coisa. Nesse ponto, quando você está registrando um erro e isso falha, a única coisa responsável a fazer é nada; você não tem garantia de que tudo o que tentar não a tornará pior.
GWLlosa

Ótimo ponto. No meu código de log, também gosto de adicionar o InnerMessage para a exceção. Muitas vezes, isso é nulo; portanto, tentar adicioná-lo explodiria o próprio log.
Tangurena 5/11/10

@Tangurena Se geralmente é nulo, lide com isso, não estrague. Em C # eu costumo guardar essas coisas por ?? "";
Loren Pechtel 01/01/19

8

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 switchinstrução, catchbloco 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.


6

catchBlocos 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:

  • não faça nada sobre a exceção. Será capturado e manipulado por uma camada diferente
  • pegue-o e execute a ação corretiva.
  • pegue, faça alguma coisa, mas jogue novamente para outra camada

Isso realmente não deixa espaço para catchblocos 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 catchbloco vazio em um programa escrito nesse idioma é muito parecido com um elsebloco vazio em um idioma tradicional (ou nenhum elsebloco). No entanto, acredito que esse não é o estilo de programação recomendado pelas comunidades de desenvolvimento em C #, Java ou C ++.


Eu acho que o uso de exceções como controle lógico se tornou bastante comum, então me encontro com blocos de captura vazios ou quase vazios com bastante frequência.
Whatsisname

+1 por reconhecer que um bloco de captura vazio pode indicar o uso de exceções para controle de fluxo.
John M Gant

O uso de exceções para a lógica de programação é geralmente considerado uma prática ruim. Mas, surpreendentemente (meu argumento original contra exceções), as exceções não sofrem um impacto perceptível no desempenho. Consulte codeproject.com/KB/exception/ExceptionPerformance.aspx .
quer

6

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.


11
Em casos como esse, ficaria tentado a adicionar throw new Error(e)apenas para ter certeza absoluta de que não perdi nada (ou relatei uma plataforma realmente quebrada).
Halle Knast

@HalleKnast Sim. Isso foi discutido em detalhes aqui: softwareengineering.stackexchange.com/questions/122233/…
user281377

5

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;

3

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.


2

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.


2

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
}

+1, eu odeio fazer isso, mas ele funciona no JUnit
sal

@sal: e o @Test (esperado = Exception.class)?
ysolik

@ysolik, mau hábito
sal

11
@ sal: Por que isso é um mau hábito?
Adam Lear

ummmm. existem maneiras de fazer isso com estruturas de teste de unidade. Ex. NUnit. Há certos casos em que testar se algo previsivelmente falha pode ser útil. Embora, eu nunca faria isso em código de produção.
quer

2

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.


1

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.


E se _log.Debug lançar uma exceção (por qualquer motivo)?
precisa saber é

Então .Debug () come a exceção e eu nunca soube disso. Mas sinto-me muito mais confiante em relação ao meu criador de logs do que ao código que geralmente gera exceções. Se eu estivesse preocupado com isso, suponho que poderia implementar algum tipo de padrão que observe a entrada no bloco catch e salve-o para mais tarde. Em qualquer caso - ponto de vista
Tim Claason 4/11

1

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 finallyclá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


Outro exemplo: no nodejs, o método fs.exists (que retorna true ou false) está obsoleto ( nodejs.org/dist/latest-v10.x/docs/api/… ) em favor do fs.access que gera uma exceção em caso um arquivo não exista. Se alguém quiser fazer algo apenas se o arquivo existir, a cláusula catch é totalmente desnecessária.
systemovich 17/07

0

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 ...)


0

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.


Isso não significa que você não faz nada, apenas que o que você faz não aborta o fluxo. Capte suas exceções, salve-as na memória (sem atraso na gravação do disco) e, em seguida, acabe com a energia em uma declaração final. Em seguida, faça o que quiser com as informações salvas.
Loren Pechtel 01/01/19

0

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.