É sempre bom ter uma instrução catch vazia?

53

Pensei nisso e não pude dar um exemplo. Por que alguém iria querer pegar uma exceção e não fazer nada sobre isso? Você pode dar um exemplo? Talvez seja apenas algo que nunca deveria ser feito.

    
por ysolik 04.11.2010 / 15:08
fonte

15 respostas

74

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 catch devem ter alguma coisa , mesmo que esse seja apenas um comentário explicando por que você está ignorando a exceção.

Edit: Eu também quero enfatizar que, se você vai fazer algo assim, você deve ter cuidado para pegar apenas a exceção específica que você deseja ignorar. Fazer um antigo catch {} é quase sempre uma coisa ruim.

    
por 04.11.2010 / 16:24
fonte
49

Um exemplo em que acho que não há problema em engolir exceção sem fazer nada, mesmo registrando a exceção, está dentro do próprio código de registro.

Se você tentou fazer log de alguma coisa e recebeu uma exceção, não há muito o que fazer:

  • você não pode logar, claro;
  • você pode voltar para 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 das aplicações;
  • lançar exceção vai fazer pouco bem, pois vai acabar em uma instrução catch até a pilha que quase certamente tentará registrá-lo ...

Isso deixa você com a opção "menos do mal" de engolir tranquilamente, permitindo que o aplicativo continue executando seu trabalho principal, mas comprometendo a capacidade de solucionar o problema.

    
por 04.11.2010 / 20:03
fonte
8

Se uma exceção for lançada por uma operação que seja opcional, 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 se pareça com um bug (queda em uma instrução switch , bloco catch vazio, atribuição em uma condição).

Você pode argumentar que esse não é um uso adequado de exceções, mas isso é para outra pergunta.

    
por 04.11.2010 / 16:16
fonte
6

Os blocos catch 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 consequência da adoção dessa abordagem, quando você programa uma camada de aplicativo específica, você tem várias opções:

  • não faz nada sobre a exceção. Ele será capturado e manipulado por uma camada diferente
  • pegue e execute a ação corretiva.
  • pegue, faça algo, mas volte a jogá-lo para outra camada para lidar com

Isso não deixa espaço para nada, blocos catch vazios.

EDITADO PARA ADICIONAR : suponha que você esteja programando em um idioma onde lançar exceções é a maneira normal de controlar a lógica do programa (uma das alternativas para goto ). Em seguida, um bloco catch vazio em um programa escrito nessa linguagem é muito parecido com um bloco else vazio em um idioma tradicional (ou nenhum bloco else ). No entanto, acredito que este não seja o estilo de programação recomendado pelas comunidades de desenvolvimento C #, Java ou C ++.

    
por 04.11.2010 / 16:52
fonte
6

Em alguns casos, o Java exige que você manipule uma exceção que, sob nenhuma circunstância, jamais acontecerá. Considere este código:

try {
   bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}

Every implementation of the Java platform is required to support the following standard charsets.

...

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.

    
por 04.11.2010 / 22:12
fonte
5

Como regra geral, não. Você quer jogar a exceção na pilha ou registrar o que aconteceu.

No entanto, geralmente faço isso quando estou analisando strings e convertendo em números (especialmente em projetos de mapeamento que são do tipo use uma vez e descarte).

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;
    
por 04.11.2010 / 15:41
fonte
2

Em alguns casos, a exceção não é de todo excepcional; na verdade, é 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 existe um método isAlive () em java.lang.Process (), a única maneira de verificar se ainda está ativo é chamar exitValue (), que lança uma IllegalThreadStateException se o processo ainda estiver em execução. / p>     

por 10.11.2010 / 10:43
fonte
2

Na minha humilde experiência, raramente isso é bom. Sua milhagem pode variar.

Quase toda vez que eu vi isso em nossa base de código, foi porque eu localizei um bug que a exceção comido escondeu. Em outras palavras, ao não fornecer nenhum código, o aplicativo não tem como indicar um erro ou realizar outra ação.

Às vezes, em camadas da API, por exemplo, você pode não querer ou permitir que exceções passem, então, nesse caso, tento tentar capturar as mais genéricas e, em seguida, registrá-las. Mais uma vez, para pelo menos dar uma chance à sessão de depuração / diagnóstico.

Normalmente, se deve estar vazio, o mínimo que eu faço é colocar uma declaração de algum tipo, então pelo menos algo acontece durante uma sessão de depuração. Dito isto, se eu não consigo lidar com isso, eu costumo omiti-los completamente.

    
por 04.11.2010 / 15:42
fonte
2

IMO existe um lugar onde instruções catch vazias sã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
}
    
por 04.11.2010 / 19:04
fonte
2

Acredito que existam 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 sendo executado se uma operação específica falhar. No entanto, eu acho que esse padrão pode ser facilmente usado em demasia, então 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 isso pode levar a alguma outra parte do código que falhe mais tarde.

    
por 04.11.2010 / 19:43
fonte
1

Eu 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 melhorar o código? Se uma exceção ocorre 10% do tempo, e você nunca sabe sobre isso, a exceção será sempre tão ruim ou pior.

No entanto, vejo o outro lado, que se a exceção não causar danos reais no processamento de código, então por que expor o usuário a ele. A solução que eu geralmente implemento é tê-lo logado pela minha classe logger (que é em cada classe que eu escrevo no trabalho).

Se for uma exceção benigna, faço uma chamada para o método .Debug () do meu Agente de Log; caso contrário, Logger.Error () (e (talvez) lançará).

try
{
   doWork();
}
catch(BenignException x)
{
  _log.Debug("Error doing work: " + x.Message);
}

A propósito, na minha implementação do meu registrador, fazer uma chamada para o método .Debug () somente será registrado se meu arquivo App.Config especificar que o nível de log de execução do programa está no modo de Depuração.

    
por 04.11.2010 / 17:14
fonte
0

A única vez que realmente precisei fazer algo assim foi com uma classe de log para um aplicativo de console. Houve 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 foi que, às vezes, o registro no arquivo falhou. Permissões de diretório impediram você de escrever o arquivo, o arquivo foi bloqueado exclusivamente, o que for. Se isso acontecesse, eu não poderia lidar com a exceção da mesma forma que eu tinha em outro lugar (pegar, registrar os detalhes relevantes , agrupar em um tipo de exceção melhor, etc) porque registrar os detalhes da exceção causou o logger para percorrer as mesmas ações (tentando gravar em um arquivo) que já haviam falhado. Quando eles falharam de novo ... Você vê onde eu estou indo. Ele entra em um loop infinito.

Se você soltar alguns blocos try {}, poderá acabar com a exceção exibida em uma caixa de mensagem (não com o que você deseja para um aplicativo de configuração silenciosa). Então, nesse método que grava no arquivo de log, eu tenho um manipulador de captura vazio. Isso não enterra o erro, pois você ainda recebe o código de erro > 0, ele 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 mais cedo, eu estava com medo de ser levado ao esquecimento. Já que eu não sou o único, entretanto ...)

    
por 04.11.2010 / 16:20
fonte
0

Não há problema quando uma sequência deve ser executada, não importa qual seja a peça mais crítica colocada no final.

Por exemplo. Na comunicação de controle de movimento do host para o controlador. Em situações de emergência, há uma série de etapas de preparação para abortar o movimento, parar a geração de trajetória, desacelerar os motores, permitir quebras, desabilitar o amplificador e, depois disso, você deve matar a energia do barramento. Tudo deve acontecer sequencialmente e se um dos passos falhar, o restante dos passos deve ser executado de qualquer maneira, mesmo com o risco aceito de danificar o hardware. Diga que, mesmo que tudo acima falhe, o poder de matar o barramento deve acontecer de qualquer maneira.

    
por 05.11.2010 / 01:09
fonte
0

Fechando os recursos do sistema normalmente para se recuperar de um erro

Por exemplo. Se você estiver executando um serviço em segundo plano que não fornece feedback para o usuário, talvez não queira enviar uma indicação do erro para o usuário, mas é necessário fazer fechar os recursos sendo usado de uma maneira graciosa.

try
{
    // execution code here
}
catch(Exception e)
{
    // do nothing here
}
finally
{
    // close db connection
    // close file io resource
    // close memory stream
    // etc...
}

O ideal seria usar a captura no modo de depuração para capturar erros e imprimi-los no console ou em um arquivo de log para testes. Em um ambiente de produção, espera-se que os serviços de segundo plano não interrompam o usuário quando um erro é acionado, portanto a saída de erro deve ser suprimida.

    
por 20.11.2010 / 02:43
fonte
0

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 cláusula finally é 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 capturas no ECMAScript que diz respeito a isto e casos de uso semelhantes.

Referências

por 19.09.2018 / 15:44
fonte

Tags