Existe algum motivo válido para ignorar uma exceção detectada

Uau, acabei de receber de volta um grande projeto em C # de desenvolvedores terceirizados e, enquanto passava pela minha revisão de código, minha ferramenta de análise revelava um monte de coisas que considerava ruins. Uma das mensagens mais desanimadoras foi:

Exceptions.DontSwallowErrorsCatchingNonspecificExceptionsRule : 2106 defects 

Os desenvolvedores me garantiram que eles tinham uma boa razão para todos os blocos de captura vazios, que às vezes a tentativa com blocos de captura vazios está lá apenas para ignorar exceções inúteis e evitar que o aplicativo falhe. Eu sinto que isso é um policial e completo BS. Alguns dos exemplos que realmente pesquisei foram chamadas de database em que o registro estava sendo salvo no database e, nesse caso, se uma exceção fosse ignorada, o usuário receberia um aviso de aprovação, achava que tudo estava bem e continuasse com trabalho deles. Na realidade, o trabalho deles nunca foi salvo. Eu acho que isso é absolutamente o tipo mais horrível de erro. Nesse caso, eles estão completamente errados em lançar esse código em uma tentativa com um bloco catch vazio. Mas a minha pergunta é: “Isso é aceitável em qualquer situação?” Eu acho que não, mas eu sou conhecido por estar errado.

Embora existam algumas razões razoáveis ​​para ignorar exceções; no entanto, geralmente são apenas exceções específicas que você pode ignorar com segurança. Como observado por Konrad Rudolph , você pode ter que pegar e engolir um erro como parte de um framework; e como observado pelo osp70 , pode haver uma exceção gerada por uma estrutura que você sabe que pode ignorar.

Em ambos os casos, você provavelmente conhecerá o tipo de exceção e, se conhecer o tipo, deverá ter um código semelhante ao seguinte:

 try { // Do something that might generate an exception } catch (System.InvalidCastException ex) { // This exception is safe to ignore due to... } catch (System.Exception ex) { // Exception handling } 

No caso da sua aplicação, parece que algo semelhante pode ser aplicado em alguns casos; mas o exemplo que você dá de um database salvar retornando um “OK” mesmo quando há uma exceção não é um sinal muito bom.

Eu não entendo exceções a menos que eu planeje fazer algo sobre elas. Ignorá-los não está fazendo algo sobre eles.

Eu às vezes uso um WebControl que não é obrigatório para uma página a ser exibida. Se falhar, não quero impedir a exibição da página. Um exemplo de um WebControl não crítico seria aquele que exibe um anúncio.

No entanto, eu registro o erro. Eu apenas não recuso.

Meu sentimento é que qualquer bloco de captura vazio precisa de um comentário.

Possivelmente é válido ignorar certos erros, mas você precisa documentar suas razões.

Além disso, você não gostaria de torná-lo um genérico “catch (Exception e) {}”.

Você deve capturar apenas o tipo de erro específico esperado e sabe-se que ele é ignorado com segurança.

Geralmente não, na verdade não em 99% de todos os casos, MAS

Existem exceções. Um projeto em que trabalhei usámos uma biblioteca de terceiros para lidar com um dispositivo TWAIN. Foi buggy e sob algumas combinações de hardware iria lançar um erro de ponteiro nulo. No entanto, nunca encontramos nenhuma circunstância em que, na verdade, não conseguimos digitalizar o documento antes que ele fizesse isso – então, capturar a exceção era inteiramente racional.

Então eu acho que se é o seu código que está lançando a exceção, então você deve sempre verificar, mas se você está preso com código de terceiros, em algumas circunstâncias você pode ser forçado a comer a exceção e seguir em frente.

O outro caso em que você pode ser desculpado por pegar e ignorar exceções é quando você está testando a unidade.

 public void testSomething(){ try{ fooThatThrowsAnException(parameterThatCausesTheException); fail("The method didn't throw the exception that we expected it to"); } catch(SomeException e){ // do nothing, as we would expect this to happen, if the code works OK. } } 

Observe que, mesmo que o bloco catch não faça nada, isso explica por quê.

Dito isto, os frameworks de testes mais recentes (Junit4 e TestNG) permitem que você especifique a exceção esperada – o que leva a algo assim …

 @Test(expected = SomeException.class) public void testSomething(){ fooThatThrowsAnException(parameterThatCausesTheException); fail("The method didn't throw the exception that we expected it to"); } 

Eu acho que pelo que eu entendi a melhor resposta é que pode ser um pouco aceitável, mas deve ser limitada. Você deve tentar usar outra alternativa, e se você não conseguir encontrar outra alternativa, você deve saber o suficiente sobre como o código funciona que você pode esperar tipos de exceção específicos e não apenas usar o cobertor pegar todos os “Exception”. A documentação do motivo pelo qual essa exceção é ignorada deve ser incluída na forma de um comentário compreensível.

em código crítico, provavelmente não, porque o estado do programa sempre deve ser exatamente definido. como o seu exemplo de chamada de database.

em código não-crítico, claro, também fazemos isso (apenas exibimos exceções capturadas em uma checkbox de mensagem e continuamos em execução). talvez um plugin ou módulo esteja falhando, mas o programa principal não é afetado. talvez um lexical_cast falhe e haja uma falha de texto na canvas. Não há necessidade de interromper o processo.

Um exemplo de onde eu considero isso aceitável é em algum módulo não crítico de uma aplicação crítica (digamos, no módulo de feedback de som do sistema de navegação do ônibus espacial), para exceções que nunca deveriam acontecer, e não podem ser tratadas de forma mais limpa .

Nesses casos, você não gostaria de permitir que a exceção se propagasse e fizesse com que o aplicativo inteiro falhasse (Desculpe, não há mais sistema de navegação, nosso módulo de aviso sonoro caiu e não havia nada que pudéssemos fazer a respeito).

Editado para dizer que em qualquer um desses casos, pelo menos você deseja registrar o evento em algum lugar.

Eu acho que se você tem um bloco catch vazio você precisa documentar porque está vazio para que o próximo desenvolvedor saiba. Por exemplo, em server.transfer, uma exceção da web às vezes é lançada. Eu entendo isso e comente que podemos ignorá-lo por causa da chamada de transferência.

Certamente há circunstâncias em que não há problema em pegar uma exceção específica e não fazer nada. Aqui está um exemplo trivial:

  public FileStream OpenFile(string path) { FileStream f = null; try { f = new FileStream(path, FileMode.Open, FileAccess.ReadWrite); } catch (FileNotFoundException) { } return f; } 

Você também pode escrever o método desta maneira:

  public FileStream OpenFile(string path) { FileStream f = null; FileInfo fi = new FileInfo(path); if (fi.Exists) { f = new FileStream(path, FileMode.Open, FileAccess.ReadWrite); } return f; } 

Nesse caso, capturar a exceção é (muito) marginalmente mais seguro, já que o arquivo pode ser excluído entre o momento em que você verifica sua existência e a hora em que você o abre.

Há razões para não fazer isso, com certeza. No .NET, as exceções são dispendiosas do ponto de vista computacional, portanto, você quer evitar qualquer coisa que lance muitas delas. (No Python, onde as exceções são baratas, é comum usar exceções para fazer coisas como quebrar loops.)

Mas isso está ignorando uma exceção específica . Este código:

 catch { } 

é indesculpável.

Não há uma boa razão para não capturar a exceção específica digitada que o código no bloco try irá lançar. A primeira razão que o desenvolvedor ingênuo dá para capturar exceções, independentemente do tipo, “Mas eu não sei que tipo de exceção pode ser lançada”, é uma espécie de resposta à pergunta.

Se você não sabe que tipo de exceção pode ser acionado, não sabe como seu código pode falhar. Se você não sabe como seu código pode falhar, você não tem base para supor que não há problema em continuar o processamento, se isso acontecer.

Sim, é aceitável (inevitável, necessário) em certas circunstâncias, de acordo com a postagem de Maxim. Isso não significa que você tenha que gostar. 2106 violações provavelmente são demais e pelo menos deveriam ter adicionado um comentário no bloco catch sobre o porquê de ter sido aprovado para engolir essa exceção.

@Dustin É uma prática ruim mostrar detalhes de exceção para um usuário público (rastreamentos de pilha, números de linha, etc). Você provavelmente deve registrar a exceção e exibir um erro genérico.

Quando se trata de exceções, sempre há exceções .

Depende do quadro. Estruturas mal implementadas podem realmente exigir isso. Lembro-me de um hack no VB6, onde não havia como determinar se uma coleção continha um elemento. A única maneira era tentar recuperar o elemento e engolir o erro.

Acho que a pergunta original foi bem respondida, mas uma coisa que gostaria de acrescentar é que, se você acha que esses desenvolvedores terceirizados / contratados produziram um trabalho de baixa qualidade, você deve garantir que as pessoas certas em sua empresa saibam disso.

Pode haver uma chance de que ele possa ser enviado de volta para retrabalho, que o pagamento possa ser parcialmente retido ou que a mesma empresa não seja usada novamente. Se sua empresa usar contratados novamente, eles poderão encontrar uma maneira de criar requisitos de qualidade nos contratos, supondo que isso não esteja lá.

Se isso fosse trabalho interno, haveria consequências para a equipe / indivíduo que produziam um trabalho abaixo do padrão. Talvez isso significaria apenas que eles têm que trabalhar noites e fins de semana para consertá-lo, mas eles estariam no gancho para isso de uma forma ou de outra. O mesmo se aplica aos contratantes, possivelmente até mais.

Apenas tenha cuidado para explicar sua posição profissionalmente e com foco no que é melhor para a empresa / produto. Você não quer que pareça que está apenas reclamando, ou que tem algum tipo de objeção política à terceirização. Não faça isso com você. Faça sobre custo, tempo para comercializar, satisfação de cliente, etc. Você sabe, todas aquelas coisas que os tipos de administração se preocupam.

Eu me pergunto muito sobre esse específico.

 Connection con = DriverManager.getConnection(url, "***", "***"); try { PreparedStatement pStmt = con.prepareStatement("your query here"); ... // query the database and get the results } catch(ClassNotFoundException cnfe) { // real exception handling goes here } catch(SQLException sqle) { // real exception handling goes here } finally { try { con.close(); } catch { // What do you do here? } } 

Eu nunca sei o que fazer nessa última captura no bloco final. Eu nunca vi próximo () lançar uma exceção antes, e é tão improvável que eu não me preocupo com isso. Acabei de registrar a exceção e seguir em frente.

A menos que seu código catch seja

  1. Registrar a exceção
  2. Reempacote a exceção em outra exceção que corresponda à mesma abstração. e jogue novamente
  3. Lida com a exceção da maneira que você vê adequado

Você pode ignorar a exceção, mas pelo menos mencionar a exceção esperada nos documentos do método, para que o consumidor possa esperar e manipular, se necessário

Para dar um exemplo do mundo de Java, onde não há problema em ignorar uma exceção:

 String foo="foobar"; byte[] foobytes; try { foobytes=foo.getBytes("UTF-8"); } catch (UnsupportedEncodingException uee) { // This is guaranteed by the Java Language Specification not to occur, // since every Java implementation is required to support UTF-8. } 

Dito isto, mesmo em situações como esta, eu costumo usar

 ... catch (UnsupportedEncodingException uee) { // This is guaranteed by the Java Language Specification not to occur, // since every Java implementation is required to support UTF-8. uee.printStackTrace(); } 

Se a máquina virtual vai ser louca / quebra de especificações, há pouco que eu possa fazer sobre isso, mas com o rastreamento de pilha, eu pelo menos consigo perceber quando começou a sua descida à loucura …

Isso é uma coisa muito ruim de se fazer.

Embora existam razões válidas que você pode querer ignorar exceções – se é esperado de alguma forma, e não há necessidade de fazer nada sobre isso – no entanto, fazendo isso 2000 vezes parece que eles só querem varrer suas exceções para debaixo do tapete.

Exemplos de onde é aceitável engolir exceções podem ser sondar coisas … você envia uma mensagem para algum dispositivo ou módulo, mas não se importa se realmente chega lá.

O seguinte apenas se aplica a idiomas que verificaram exceções, por exemplo, Java:

Às vezes, um método gera uma exceção verificada que você sabe que não acontecerá, por exemplo, algumas APIs java esperam um nome de codificação como uma string e lançam um UnsupportedEncodingException se a codificação dada não for suportada. Mas geralmente eu passo um literal “UTF-8” que eu sei que é suportado para que eu pudesse, teoricamente, escrever uma captura vazia lá.

Em vez de fazer isso (captura vazia), eu geralmente lanço uma exceção genérica desmarcada envolvendo a exceção “impossível”, ou eu mesmo declaro uma class ImpossibleException que eu jogo. Porque minha teoria sobre essa condição de erro ser impossível pode estar errada e, nesse caso, eu não gostaria que a exceção fosse engolida.

Eu gosto de deixar quase todas as minhas exceções borbulharem para um manipulador de aplicativos onde elas estão registradas e uma mensagem de erro genérica é exibida para o usuário final. Mas a advertência aqui é que não deve haver muitas exceções que realmente ocorram. Se o seu aplicativo está lançando muitas exceções, provavelmente há algo errado ou algo que poderia ter sido melhor codificado. Na maioria das vezes, tento garantir que meu código verifique casos excepcionais avançados, pois gerar exceções é caro.

Como um aparte, terceirizar a codificação geralmente é uma má idéia. Pela minha experiência, geralmente eles são consultores que estão apenas nisso para o contracheque e não têm interesse no sucesso do projeto. Além disso, você se rende a seus padrões de falta de codificação (a menos que você inclua isso no contrato).

Pense nisso desta maneira – se você está gastando os ciclos da CPU para capturar a exceção, mas depois engolir, está ignorando um possível problema e desperdiçando a CPU. Como muitos disseram, o aplicativo não deveria estar lançando tantas exceções a menos que você tenha algo mal construído.

Nós temos um aplicativo que faz muito processamento em nome de outros aplicativos, onde você insere algum trabalho (coleção de configuração) em um database e o aplicativo irá executá-lo e executá-lo no momento apropriado. Nós tendemos a engolir muitas exceções nesse aplicativo, porque mesmo que o Job1 morra de forma horripilante com um erro catastrófico, queremos que o aplicativo continue ativo para realizar uma tentativa de processamento do Job2.

Eu acho que a melhor regra é ignorar apenas uma exceção se você está completamente ciente do que a exceção significa e as possíveis ramificações dela. No caso de algum módulo isolado que não afeta o resto do seu sistema, eu acho que seria bom apenas pegar a Exceção genérica contanto que você saiba que nada de ruim aconteça com qualquer outra coisa.

IMO é mais fácil saber as ramificações em Java, pois cada método é necessário para declarar todas as exceções que pode lançar para que você saiba o que esperar, mas em C # uma exceção pode ser lançada mesmo que não esteja documentada, por isso é difícil saber tudo as possíveis exceções que podem ser lançadas por um método, e sem esse conhecimento, geralmente é uma má idéia capturar tudo.