Por que blocos vazios de captura são uma má idéia? [fechadas]

192

Acabei de ver uma pergunta sobre try-catch , que pessoas (incluindo Jon Skeet) dizem que blocos vazios de captura são uma péssima idéia? Porque isso? Não há situação em que uma captura vazia não seja uma decisão de projeto errada?

Quero dizer, por exemplo, às vezes você deseja obter informações adicionais de algum lugar (serviço da web, banco de dados) e realmente não se importa se receberá ou não essas informações. Então você tenta obtê-lo e, se acontecer alguma coisa, tudo bem, vou adicionar um "catch (Exceção ignorada) {}" e isso é tudo

Samuel Carrijo
fonte
53
Eu escreveria um comentário explicando por que deveria estar vazio.
Mehrdad Afshari 5/08/09
5
... ou pelo menos registrar que foi capturado.
Matt b
2
@ ocdecio: evite-o para código de limpeza , não evite-o em geral.
John Saunders
1
@ocdecio: Outro caso para evitar o uso do try..catch é quando você captura exceções para tipos conhecidos de falhas. por exemplo, exceções de conversão numérica
MPritchard
1
@ocdecio - try..finally é melhor do que try..empty catch porque o erro continua na pilha - para ser tratado pela estrutura ou para matar o programa e ser exibido ao usuário - ambos os resultados são melhores do que um " falha silenciosa ".
Nate

Respostas:

297

Normalmente, try-catch vazio é uma má idéia, porque você está engolindo silenciosamente uma condição de erro e, em seguida, continua a execução. Ocasionalmente, isso pode ser a coisa certa a ser feita, mas geralmente é um sinal de que um desenvolvedor viu uma exceção, não sabia o que fazer e, portanto, usou uma captura vazia para silenciar o problema.

É o equivalente de programação de colocar fita preta sobre uma luz de aviso do motor.

Acredito que a maneira como você lida com exceções depende de qual camada do software você está trabalhando: Exceções na Floresta Tropical .

Ned Batchelder
fonte
4
Nessas circunstâncias realmente raras em que realmente não preciso da exceção e o registro não está disponível por algum motivo, certifique-se de comentar que é intencional - e por que não é necessário, e reitero que ainda prefiro registrá-lo se era viável naquele ambiente. Basicamente, faço o comentário MAIS funcionar do que o log teria sido se tivesse sido uma opção nessa circunstância. Felizmente, tenho apenas dois clientes para os quais esse é um problema, e é apenas ao enviar e-mails não críticos de seus sites.
John Rudy
37
Também adoro a analogia - e, como todas as regras, há exceções. Por exemplo, é perfeitamente aceitável usar fita preta no relógio que pisca no seu videocassete se você nunca pretende gravar gravações programadas (para todos os velhos que se lembram do que é um videocassete).
227 Michael Burr
3
Como qualquer desvio da prática aceita, faça-o se apropriado e documente-o para que o próximo sujeito saiba o que você fez e por quê.
David Thornley
5
@ Jason: no mínimo, você deve incluir um comentário detalhado explicando por que está silenciando as exceções.
Ned Batchelder
1
Esta resposta assume duas coisas: 1. Que a exceção lançada é realmente significativa e que quem escreveu o código que jogava sabia o que estava fazendo; 2. Que a exceção é realmente uma "condição de erro" e não é abusada como fluxo de controle (embora este seja um caso mais específico do meu primeiro ponto).
Boris B.
37

Eles são uma péssima idéia em geral, porque é uma condição verdadeiramente rara em que uma falha (condição excepcional, mais genericamente) é atendida adequadamente, sem resposta alguma. Além disso, os catchblocos vazios são uma ferramenta comum usada por pessoas que usam o mecanismo de exceções para verificação de erros que deveriam estar executando preventivamente.

Dizer que sempre é ruim é falso ... isso é verdade de muito pouco. Pode haver circunstâncias em que você não se importa com o erro ou a presença do erro de alguma forma indica que você não pode fazer nada a respeito (por exemplo, ao gravar um erro anterior em um arquivo de log de texto e você recebe um IOException, o que significa que você não pode escrever o novo erro de qualquer maneira).

Adam Robinson
fonte
11

Eu não esticaria as coisas ao ponto de dizer que quem usa blocos de captura vazios é um mau programador e não sabe o que está fazendo ...

Eu uso blocos de captura vazios, se necessário. Às vezes, o programador da biblioteca que estou consumindo não sabe o que está fazendo e lança exceções, mesmo em situações em que ninguém precisa.

Por exemplo, considere alguma biblioteca de servidores http, eu não me importaria se o servidor lançasse uma exceção porque o cliente foi desconectado e index.htmlnão pôde ser enviado.

lubos hasko
fonte
6
Você certamente está generalizando demais. Só porque você não precisa, isso não significa que ninguém precisa. Mesmo que absolutamente nada possa ser feito em resposta, existe alguém em algum lugar com a exigência de coletar estatísticas sobre conexões abandonadas. Portanto, é bastante rude supor que "o programador de biblioteca não sabe o que está fazendo".
Ben Voigt
8

Existem casos raros em que isso pode ser justificado. No Python, você costuma ver esse tipo de construção:

try:
    result = foo()
except ValueError:
    result = None

Portanto, pode ser bom (dependendo do seu aplicativo) fazer:

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

Em um projeto .NET recente, tive que escrever um código para enumerar DLLs de plug-in para encontrar classes que implementam uma certa interface. O bit de código relevante (no VB.NET, desculpe) é:

    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

Embora, mesmo nesse caso, eu admita que registrar a falha em algum lugar provavelmente seja uma melhoria.

Daniel Pryden
fonte
Mencionei o log4net como uma dessas instâncias antes de ver sua resposta.
Scott Lawrence
7

Blocos de captura vazios geralmente são inseridos porque o codificador realmente não sabe o que está fazendo. Na minha organização, um bloco de captura vazio deve incluir um comentário sobre por que não fazer nada com a exceção é uma boa idéia.

Em uma observação relacionada, a maioria das pessoas não sabe que um bloco try {} pode ser seguido com um catch {} ou finalmente {}, apenas um é necessário.

Justin
fonte
1
+1 Eu enfatizaria o 'ou' na última frase para aumentar o impacto
MPritchard 5/08/09
Esse segundo parágrafo pode depender da linguagem, certo?
David Z
3
a menos, claro, que você esteja falando do IE6 ou IE7 ;-) ... onde a captura é necessária ou, finalmente, não é executada. (isso foi corrigido no IE8)
scunliffe 5/08/09
Fonte: webbugtrack.blogspot.com/2007/11/…
scunliffe 5/08/09
Muito verdadeiro. Pode valer a pena destacar os idiomas. Acredito .net é bom
MPritchard
7

Exceções só devem ser lançadas se houver realmente uma exceção - algo acontecendo além da norma. Um bloco vazio de captura basicamente diz "algo ruim está acontecendo, mas eu simplesmente não me importo". Esta é uma má ideia.

Se você não quiser lidar com a exceção, deixe que ela se propague para cima até atingir algum código que possa lidar com ela. Se nada puder lidar com a exceção, o aplicativo deverá ser desativado.

Reed Copsey
fonte
3
Às vezes você sabe que isso não afetará nada. Então vá em frente, faça e comente para que o próximo cara não pense que você estragou tudo porque você é incompetente.
David Thornley
Sim, há um tempo e um lugar para tudo. Em geral, porém, acho que um bloco de captura vazio sendo bom é a exceção à regra - é um caso raro em que você deseja realmente ignorar uma exceção (geralmente, IMO, é o resultado de um mau uso de exceções).
Reed Copsey
2
@ David Thornley: Como você sabe que isso não afetará nada se você não inspeciona a exceção para determinar se é um erro esperado e sem sentido ou que deveria ser propagado para cima?
Dave Sherohman
2
@ Dave: Embora catch (Exception) {}seja uma má idéia, catch (SpecificExceptionType) {}pode estar perfeitamente bem. O programador DID inspecionou a exceção, usando as informações de tipo na cláusula catch.
Ben Voigt
7

Acho que tudo bem se você pegar um tipo de exceção específico, do qual você sabe que só será levantado por um motivo específico , e espera essa exceção e realmente não precisa fazer nada a respeito.

Mas mesmo nesse caso, uma mensagem de depuração pode estar em ordem.

balpha
fonte
5

Por Josh Bloch - Item 65: Não ignore as exceções do Java eficaz :

  1. Um bloco de captura vazio anula o objetivo de exceções
  2. No mínimo, o bloco catch deve conter um comentário explicando por que é apropriado ignorar a exceção.
KrishPrabakar
fonte
3

Um bloco de captura vazio está basicamente dizendo "Não quero saber quais erros são lançados, apenas os ignorarei".

É semelhante ao VB6 On Error Resume Next, exceto que qualquer coisa no bloco try após a exceção ser lançada será ignorada.

O que não ajuda quando algo então quebra.

Powerlord
fonte
Na verdade, eu diria que "No erro retomar próximo" é pior - ele tentará a próxima linha independentemente. Um catch vazio vai saltar para passar a chave de fechamento da instrução try
MPritchard
Bom ponto. Eu provavelmente deveria notar isso na minha resposta.
Powerlord 5/08/09
1
Isso não é exatamente verdade, se você tem um try ... catch vazio com um tipo de exceção específica, em seguida, ele irá ignorar simplesmente este tipo de exceção, e que pode ser legítima ...
Meta-Knight
Mas se você sabe que um tipo específico de exceção está sendo lançado, parece que você pode ter informações suficientes para escrever sua lógica de maneira a evitar o uso de try-catch para lidar com a situação.
Scott Lawrence
@ Scott: Certas linguagens (por exemplo, Java) verificaram exceções ... exceções para as quais você é forçado a escrever blocos de captura.
Powerlord 5/08/09
3

Isso ocorre em conjunto com "Não use exceções para controlar o fluxo do programa" e "Somente use exceções para circunstâncias excepcionais". Se isso for feito, as exceções só deverão ocorrer quando houver um problema. E se houver algum problema, você não deseja falhar silenciosamente. Nas raras anomalias em que não é necessário lidar com o problema, você deve pelo menos registrar a exceção, caso a anomalia não se torne mais uma anomalia. A única coisa pior do que falhar é falhar silenciosamente.

Imagist
fonte
2

Eu acho que um bloco de captura completamente vazio é uma má idéia, porque não há como inferir que ignorar a exceção foi o comportamento pretendido do código. Não é necessariamente ruim engolir uma exceção e retornar falso ou nulo ou algum outro valor em alguns casos. A estrutura .net possui muitos métodos "try" que se comportam dessa maneira. Como regra geral, se você engolir uma exceção, adicione um comentário e uma instrução de log se o aplicativo suportar o log.

complexcipher
fonte
1

Como se uma exceção for lançada, você nunca a verá - falhar silenciosamente é a pior opção possível - você terá um comportamento incorreto e não fará ideia de onde isso está acontecendo. Pelo menos coloque uma mensagem de log lá! Mesmo que seja algo que 'nunca possa acontecer'!

Nate
fonte
1

Blocos de captura vazios são uma indicação de um programador que não sabe o que fazer com uma exceção. Eles estão suprimindo a exceção de possivelmente borbulhar e serem manipulados corretamente por outro bloco de tentativa. Sempre tente fazer algo com a exceção que você está capturando.

Dan
fonte
1

Acho que o mais irritante com instruções de captura vazias é quando algum outro programador fez isso. O que quero dizer é que quando você precisa depurar o código de outra pessoa, qualquer instrução catch vazia torna essa tarefa mais difícil do que precisa. As instruções catch do IMHO sempre devem mostrar algum tipo de mensagem de erro - mesmo que o erro não seja tratado, ele deve pelo menos detectá-lo (alt. Ativado apenas no modo de depuração)

AndersK
fonte
1

Provavelmente nunca é a coisa certa, porque você passa silenciosamente todas as exceções possíveis. Se você está esperando uma exceção específica, deve testá-la e tentar novamente, se não for sua exceção.

try
{
    // Do some processing.
}
catch (FileNotFound fnf)
{
    HandleFileNotFound(fnf);
}
catch (Exception e)
{
    if (!IsGenericButExpected(e))
        throw;
}

public bool IsGenericButExpected(Exception exception)
{
    var expected = false;
    if (exception.Message == "some expected message")
    {
        // Handle gracefully ... ie. log or something.
        expected = true;
    }

    return expected;
}
xanadont
fonte
1
Esse não é realmente um padrão padrão que você aconselha as pessoas a usarem lá. A maioria das pessoas captaria as Exceções de qualquer tipo que elas estejam esperando, e não as que não são. Eu posso ver algumas circunstâncias em que a sua abordagem seria válido, basta ter cuidado de postagem em um fórum como este, onde as pessoas tendem a ser readng coisas como esta, porque eles não entendem, em primeiro lugar;)
MPritchard
Minha intenção não era que IsKnownException () estivesse verificando o tipo da exceção (sim, você deve fazer isso por diferentes blocos catch), mas está verificando se é uma exceção esperada. Vou editar para deixar mais claro.
Xanadont 5/08/09
Eu estava originalmente pensando em COMExceptions. Você pode testar um ErrorCode específico e manipular os códigos que você espera. Faço isso frequentemente ao programar com ArcOjbects.
Xanadont 5/08/09
2
-1 Tomar decisões no código com base na mensagem de exceção é uma péssima idéia. A mensagem exata raramente é documentada e pode mudar a qualquer momento; é um detalhe de implementação. Ou a mensagem pode ser baseada em uma cadeia de recursos localizáveis. De qualquer forma, é apenas para ser lido por um ser humano.
Wim Coenen
Eek. A exceção.Message pode ser localizada e, portanto, não o que você espera. Isso está errado.
frankhommers
1

Geralmente, você só deve capturar as exceções que realmente pode tratar. Isso significa ser o mais específico possível ao capturar exceções. Capturar todas as exceções raramente é uma boa idéia e ignorar todas as exceções é quase sempre uma péssima idéia.

Só consigo pensar em alguns casos em que um bloco de captura vazio tem algum objetivo significativo. Se qualquer exceção específica que você estiver capturando for "manipulada", apenas tentando novamente a ação, não haverá necessidade de fazer nada no bloco catch. No entanto, ainda seria uma boa idéia registrar o fato de que a exceção ocorreu.

Outro exemplo: o CLR 2.0 mudou a maneira como as exceções não tratadas no thread do finalizador são tratadas. Antes da versão 2.0, o processo podia sobreviver a esse cenário. No CLR atual, o processo é encerrado no caso de uma exceção não tratada no encadeamento do finalizador.

Lembre-se de que você só deve implementar um finalizador se realmente precisar de um e, mesmo assim, deve fazer o mínimo possível no finalizador. Mas se o trabalho que seu finalizador deve executar pode gerar uma exceção, você precisa escolher entre o menor dos dois males. Deseja encerrar o aplicativo devido à exceção não tratada? Ou você deseja prosseguir em um estado mais ou menos indefinido? Pelo menos em teoria, o último pode ser o menor de dois males em alguns casos. Nesses casos, o bloco de captura vazio impediria o término do processo.

Brian Rasmussen
fonte
1
Quero dizer, por exemplo, às vezes você deseja obter informações adicionais de algum lugar (serviço da web, banco de dados) e realmente não se importa se receberá ou não essas informações. Então você tenta obtê-lo e, se acontecer alguma coisa, tudo bem, vou adicionar um "catch (Exceção ignorada) {}" e isso é tudo

Então, seguindo o seu exemplo, é uma má ideia nesse caso, porque você está capturando e ignorando todas as exceções. Se você estivesse pegando apenas EInfoFromIrrelevantSourceNotAvailablee ignorando, tudo bem, mas você não está. Você também está ignorando ENetworkIsDown, o que pode ou não ser importante. Você está ignorando ENetworkCardHasMeltede EFPUHasDecidedThatOnePlusOneIsSeventeen, que são quase certamente importantes.

Um bloco de captura vazio não é um problema se estiver configurado para capturar (e ignorar) exceções de certos tipos que você sabe que não são importantes. As situações em que é uma boa idéia suprimir e ignorar silenciosamente todas as exceções, sem parar para examiná-las primeiro e verificar se são esperadas / normais / irrelevantes ou não, são extremamente raras.

Dave Sherohman
fonte
1

Há situações em que você pode usá-las, mas elas devem ser muito raras. As situações em que eu poderia usar um incluem:

  • registro de exceção; dependendo do contexto, você pode querer publicar uma exceção não tratada ou uma mensagem.

  • situações técnicas em loop, como renderização ou processamento de som ou um retorno de chamada da caixa de listagem, em que o próprio comportamento demonstrará o problema, lançar uma exceção apenas atrapalhará e o registro da exceção provavelmente resultará em milhares de mensagens "com falha no XXX" .

  • programas que não podem falhar, embora ainda devam ao menos registrar alguma coisa.

para a maioria dos aplicativos winforms, descobri que basta ter uma única instrução try para cada entrada do usuário. Eu uso os seguintes métodos: (AlertBox é apenas um invólucro rápido MessageBox.Show)

  public static bool TryAction(Action pAction)
  {
     try { pAction(); return true; }
     catch (Exception exception)
     {
        LogException(exception);
        return false;
     }
  }

  public static bool TryActionQuietly(Action pAction)
  {
     try { pAction(); return true; }
     catch(Exception exception)
     {
        LogExceptionQuietly(exception);
        return false;
     }
  }

  public static void LogException(Exception pException)
  {
     try
     {
        AlertBox(pException, true);
        LogExceptionQuietly(pException);
     }
     catch { }
  }

  public static void LogExceptionQuietly(Exception pException)
  {
     try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
  }

Então, todo manipulador de eventos pode fazer algo como:

  private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
  {
     EditorDefines.TryAction(Dispose);
  }

ou

  private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
  {
     EditorDefines.TryActionQuietly(() => Render(pEventArgs));
  }

Teoricamente, você pode ter o TryActionSilently, que pode ser melhor para renderizar chamadas, para que uma exceção não gere uma quantidade infinita de mensagens.

Dave Cousineau
fonte
1

Se você não sabe o que fazer no bloco catch, basta registrar essa exceção, mas não a deixe em branco.

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }
Andzej Maciusovic
fonte
Por que isso foi prejudicado?
Vino
0

Você nunca deve ter um bloco de captura vazio. É como esconder um erro que você conhece. No mínimo, você deve escrever uma exceção em um arquivo de log para revisar mais tarde, se você pressionar o tempo.

Chadit
fonte