enquanto mantém o código do meu colega até mesmo de alguém que afirma ser um desenvolvedor sênior, muitas vezes vejo o seguinte código:
try
{
//do something
}
catch
{
//Do nothing
}
ou às vezes eles escrevem informações de log em arquivos de log como o seguinte try catch
bloco
try
{
//do some work
}
catch(Exception exception)
{
WriteException2LogFile(exception);
}
Só estou pensando se o que eles fizeram é a melhor prática? Isso me deixa confuso porque, na minha opinião, os usuários devem saber o que acontece com o sistema.
Por favor me dê alguns conselhos.
NullReference
ouArgumentNull
não faz parte do fluxo do aplicativo), significa que há um bug que precisa ser corrigido, portanto, registrá-los ajudará a depurar seu código muito mais rapidamente.Respostas:
Minha estratégia de tratamento de exceções é:
Para capturar todas as exceções não tratadas, conectando o
Application.ThreadException event
, e decida:Sempre incluo todos os trechos de código executados externamente em
try/catch
:Em seguida, incluo 'try / catch'
ApplicationException("custom message", innerException)
para acompanhar o que realmente aconteceuAlém disso, tento o meu melhor para classificar as exceções corretamente . Há exceções que:
finally
seção durante umTreeView
preenchimento)o usuário não se importa, mas é importante saber o que aconteceu. Então, eu sempre os registro:
É uma boa prática projetar alguns métodos estáticos para lidar com exceções nos manipuladores de erro de nível superior do aplicativo.
Eu também me forço a tentar:
Então finalmente:
Ruim:
Sem utilidade:
Ter uma tentativa finalmente sem capturas é perfeitamente válido:
O que faço no nível superior:
O que faço em algumas funções chamadas:
Há muito a ver com o tratamento de exceções (exceções personalizadas), mas essas regras que tento manter em mente são suficientes para os aplicativos simples que faço.
Aqui está um exemplo de métodos de extensões para lidar com exceções capturadas de uma maneira confortável. Eles são implementados de uma maneira que podem ser encadeados, e é muito fácil adicionar seu próprio processamento de exceção capturada.
fonte
catch(Exception ex) { throw ex; }
em C # é pior que redundante (independentemente do tipo de exceção que você está capturando). Para rever novamente, usethrow;
. Com o primeiro, a exceção parecerá ter se originado do seu,throw ex
enquanto que com o último, será originada corretamente dathrow
declaração original .Application.ThreadException
evento e quebra todas as exceções com umcatch(Exception ex) {ex.Log(ex);}
. Eu provavelmente concordaria que a primeira é uma prática excelente, mas a segunda acrescenta o risco de duplicar os logs de erros e oculta a ocorrência da exceção. Tambémthrow ex
é muito, muito ruim.Application.ThreadException
evento, eu não estava ciente disso, muito útil.A prática recomendada é que o tratamento de exceções nunca oculte problemas . Isso significa que os
try-catch
blocos devem ser extremamente raros.Existem três circunstâncias em que usar um
try-catch
faz sentido.Sempre lide com exceções conhecidas o mais baixo possível. No entanto, se você espera uma exceção, geralmente é uma prática melhor testá-la primeiro. Por exemplo, análise, formatação e exceções aritméticas são quase sempre melhor tratadas por verificações lógicas primeiro, do que por uma específica
try-catch
.Se você precisar fazer algo em uma exceção (por exemplo, registrar ou reverter uma transação), emita novamente a exceção.
Sempre lide com exceções desconhecidas o mais alto possível - o único código que deve consumir uma exceção e não repeti-la deve ser a interface do usuário ou a API pública.
Suponha que você esteja se conectando a uma API remota. Aqui, você espera alguns erros (e tem algumas coisas nessas circunstâncias); portanto, este é o caso 1:
Observe que nenhuma outra exceção é capturada, pois não é esperada.
Agora, suponha que você esteja tentando salvar algo no banco de dados. Temos que reverter isso se falhar, então temos o caso 2:
Observe que lançamos novamente a exceção - o código mais alto ainda precisa saber que algo falhou.
Finalmente, temos a interface do usuário - aqui não queremos exceções completamente não tratadas, mas também não queremos ocultá-las. Aqui temos um exemplo do caso 3:
No entanto, a maioria das estruturas de API ou UI possui maneiras genéricas de executar o caso 3. Por exemplo, o ASP.Net possui uma tela de erro amarela que despeja os detalhes da exceção, mas que pode ser substituída por uma mensagem mais genérica no ambiente de produção. Seguir essas é uma prática recomendada, pois economiza muito código, mas também porque o log e a exibição de erros devem ser decisões de configuração, e não codificadas.
Isso tudo significa que o caso 1 (exceções conhecidas) e o caso 3 (tratamento único da interface do usuário) têm padrões melhores (evite o erro esperado ou o tratamento manual do erro na interface do usuário).
Mesmo o caso 2 pode ser substituído por padrões melhores, por exemplo , escopos de transação (
using
blocos que revertem qualquer transação não confirmada durante o bloco) tornam mais difícil para os desenvolvedores errar o padrão de boas práticas.Por exemplo, suponha que você tenha um aplicativo ASP.Net em larga escala. O registro de erros pode ser feito via ELMAH , a exibição de erros pode ser um YSoD informativo localmente e uma boa mensagem localizada em produção. Todas as conexões com o banco de dados podem ser feitas por escopos e
using
blocos de transação . Você não precisa de um únicotry-catch
bloco.TL; DR: A melhor prática é realmente não usar
try-catch
blocos.fonte
try-catch
- pode (muito ocasionalmente) ser útil e não estou argumentando que você nunca deve usá-los, mas em 99% das vezes há uma maneira melhor.Uma exceção é um erro de bloqueio .
Antes de tudo, a melhor prática deve ser não lançar exceções para qualquer tipo de erro, a menos que seja um erro de bloqueio .
Se o erro estiver bloqueando , lance a exceção. Uma vez que a exceção já foi lançada, não há necessidade de ocultá-la porque é excepcional; informe o usuário sobre isso (você deve reformatar toda a exceção para algo útil para o usuário na interface do usuário).
Seu trabalho como desenvolvedor de software é tentar evitar um caso excepcional em que algum parâmetro ou situação de tempo de execução possa terminar em uma exceção. Ou seja, as exceções não devem ser silenciadas, mas devem ser evitadas .
Por exemplo, se você souber que alguma entrada inteira pode vir com um formato inválido, use em
int.TryParse
vez deint.Parse
. Existem muitos casos em que você pode fazer isso em vez de apenas dizer "se falhar, basta lançar uma exceção".Lançar exceções é caro.
Se, afinal, uma exceção é lançada, em vez de gravar a exceção no log depois que ela é lançada, uma das práticas recomendadas é capturá-la em um manipulador de exceção de primeira chance . Por exemplo:
Minha opinião é que as tentativas / capturas locais são mais adequadas para lidar com casos especiais em que você pode converter uma exceção em outra, ou quando você deseja "silenciá-la" para um caso muito, muito, muito, muito, muito especial (um bug na biblioteca lançando uma exceção não relacionada que você precisa silenciar para solucionar o bug inteiro).
Para o restante dos casos:
Respondendo a @thewhiteambit em algum comentário ...
@thewhiteambit disse:
Primeiro de tudo, como uma exceção não pode ser um erro?
null
enquanto o objeto era esperado => exceçãoPodemos listar 1k casos de quando uma exceção é lançada e, afinal, qualquer um dos casos possíveis será um erro .
Uma exceção é um erro, porque no final do dia é um objeto que coleta informações de diagnóstico - possui uma mensagem e acontece quando algo dá errado.
Ninguém lançaria uma exceção quando não houvesse um caso excepcional. As exceções devem bloquear os erros porque, uma vez lançadas, se você não tentar usar o try / catch e as exceções para implementar o fluxo de controle , significa que seu aplicativo / serviço interromperá a operação que entrou em um caso excepcional .
Além disso, sugiro a todos que verifiquem o paradigma à prova de falhas publicado por Martin Fowler (e escrito por Jim Shore) . Foi assim que eu sempre entendi como lidar com exceções, mesmo antes de chegar a este documento há algum tempo.
Geralmente, as exceções cortam algum fluxo de operação e são tratadas para convertê-las em erros compreensíveis pelo homem. Portanto, parece que uma exceção é realmente um paradigma melhor para lidar com casos de erro e trabalhar com eles para evitar uma falha completa do aplicativo / serviço e notificar o usuário / consumidor de que algo deu errado.
Mais respostas sobre preocupações de @whiteambit
Se seu aplicativo funcionar offline sem persistir os dados no banco de dados, você não deve usar exceções , pois a implementação do fluxo de controle
try/catch
é considerada um antipadrão. O trabalho offline é um possível caso de uso; portanto, você implementa o fluxo de controle para verificar se o banco de dados está acessível ou não, não espere até que ele esteja inacessível .A análise também é um caso esperado ( não EXCEPCIONAL CASE ). Se você espera isso, não usa exceções para controlar o fluxo! . Você obtém alguns metadados do usuário para saber qual é sua cultura e usa formatadores para isso! O .NET também oferece suporte a esse e outros ambientes, e uma exceção, porque a formatação de números deve ser evitada se você espera um uso específico de cultura do seu aplicativo / serviço .
Este artigo é apenas uma opinião ou um ponto de vista do autor.
Como a Wikipedia também pode ser apenas a opinião do (s) autor (es) do artigo, não diria que é o dogma , mas verifique o que o artigo Coding by exception diz em algum lugar de algum parágrafo:
Também diz em algum lugar:
Uso de exceção incorreto
Honestamente, acredito que o software não pode ser desenvolvido, não levando os casos de uso a sério. Se você sabe disso ...
... você não usará exceções para isso . Você apoiaria esses casos de uso usando o fluxo de controle regular.
E se algum caso de uso inesperado não for abordado, seu código falhará rapidamente, porque lançará uma exceção . Certo, porque uma exceção é um caso excepcional .
Por outro lado, e finalmente, às vezes você cobre casos excepcionais que lançam exceções esperadas , mas não os lança para implementar o fluxo de controle. Você faz isso porque deseja notificar as camadas superiores que não oferece suporte a algum caso de uso ou que seu código não funciona com alguns argumentos ou dados / propriedades do ambiente.
fonte
A única vez em que você deve preocupar seus usuários com algo que aconteceu no código é se há algo que eles podem ou precisam fazer para evitar o problema. Se eles puderem alterar dados em um formulário, pressione um botão ou altere a configuração de um aplicativo para evitar o problema e avise-os. Porém, avisos ou erros que o usuário não tem capacidade de evitar apenas os fazem perder a confiança em seu produto.
Exceções e logs são para você, o desenvolvedor, e não o usuário final. Entender a coisa certa a fazer quando você captura cada exceção é muito melhor do que apenas aplicar alguma regra de ouro ou confiar em uma rede de segurança em todo o aplicativo.
A codificação irracional é o ÚNICO tipo de codificação errada. O fato de você achar que algo melhor pode ser feito nessas situações mostra que você está investindo em boa codificação, mas evite tentar estampar alguma regra genérica nessas situações e entenda o motivo de algo ser lançado em primeiro lugar e o que você pode fazer para se recuperar dele.
fonte
Eu sei que essa é uma pergunta antiga, mas ninguém mencionou o artigo do MSDN aqui, e foi o documento que realmente o esclareceu, o MSDN tem um documento muito bom sobre isso, você deve capturar exceções quando as seguintes condições forem verdadeiras:
Sugiro ler a seção inteira " Tratamento de exceções e exceções " e também as práticas recomendadas para exceções .
fonte
A melhor abordagem é a segunda (aquela na qual você especifica o tipo de exceção). A vantagem disso é que você sabe que esse tipo de exceção pode ocorrer no seu código. Você está lidando com esse tipo de exceção e pode retomar. Se alguma outra exceção surgir, isso significa que algo está errado, o que o ajudará a encontrar erros no seu código. O aplicativo eventualmente trava, mas você saberá que há algo que você perdeu (bug) que precisa ser corrigido.
fonte
Com exceções, tento o seguinte:
Primeiro, pego tipos especiais de exceções, como divisão por zero, operações de E / S e assim por diante e escrevo código de acordo com isso. Por exemplo, uma divisão por zero, dependendo da proveniência dos valores que eu poderia alertar o usuário (por exemplo, uma calculadora simples em que em um cálculo intermediário (não os argumentos) chega em uma divisão por zero) ou para tratar silenciosamente essa exceção, registrando e continue processando.
Então tento capturar as exceções restantes e registrá-las. Se possível, permita a execução do código, caso contrário, alerte o usuário que ocorreu um erro e peça para ele enviar um relatório de erro.
No código, algo como isto:
fonte
0
prévia de um numerador, em vez de atry-catch
. Também por que pegar o genéricoException
aqui? É melhor deixar o erro aparecer do que lidar com ele aqui em todos os casos em que você não o espera.A segunda abordagem é boa.
Se você não deseja mostrar o erro e confundir o usuário do aplicativo, mostrando uma exceção de tempo de execução (ou seja, erro) que não está relacionada a eles, basta registrar o erro e a equipe técnica poderá procurar o problema e resolvê-lo.
Eu recomendo que você faça a segunda abordagem para todo o aplicativo.
fonte
catch
Os blocos sempre devem chamarthrow
para exibir a exceção ou retornar algo / exibir algo que informe ao usuário que a ação falhou. Você deseja receber a chamada de suporte quando eles falham em salvar o que quer que seja, seis meses depois, quando tentam recuperá-lo e não conseguem encontrá-lo.Deixar o bloco de captura em branco é a pior coisa a fazer. Se houver um erro, a melhor maneira de lidar com isso é:
fonte
Para mim, lidar com exceção pode ser visto como regra de negócios. Obviamente, a primeira abordagem é inaceitável. O segundo é melhor e pode ser 100% correto, se o contexto o indicar. Agora, por exemplo, você está desenvolvendo um suplemento do Outlook. Se você adicionar um exceção não tratada, o usuário do Outlook agora poderá conhecê-lo, pois o Outlook não se destruirá por causa de uma falha no plug-in. E você tem dificuldade para descobrir o que deu errado. Portanto, a segunda abordagem, neste caso, para mim, é correta. Além de registrar a exceção, você pode decidir exibir uma mensagem de erro para o usuário - eu a considero uma regra de negócios.
fonte
A melhor prática é lançar uma exceção quando o erro ocorrer. Porque ocorreu um erro e não deve ser oculto.
Mas na vida real você pode ter várias situações em que deseja ocultar isso
fonte
Exception
. Sempre. Lance uma subclasse apropriada deException
tudo que você deseja, mas nuncaException
porque isso não fornece absolutamente nenhuma informação semântica. Eu não consigo ver um cenário em que faça sentido lançar,Exception
mas não uma subclasse dele.Você deve considerar estas Diretrizes de design para exceções
https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions
fonte
O
catch
sem argumentos é simplesmente comer a exceção e é inútil. E se ocorrer um erro fatal? Não há como saber o que aconteceu se você usar catch sem argumento.Uma declaração de captura deve capturar exceções mais específicas, como,
FileNotFoundException
então, no final, você deve capturarException
qual capturaria qualquer outra exceção e registrá-las.fonte
catch(Exception)
no final? Se você não está esperando, é sempre recomendável repassar para a próxima camada.Às vezes, você precisa tratar exceções que não dizem nada aos usuários.
Meu jeito é:
Definitivamente, não precisa ser uma prática recomendada. ;-)
fonte
Eu posso lhe dizer uma coisa:
O snippet nº 1 não é aceitável porque está ignorando a exceção. (está engolindo como se nada tivesse acontecido).
Portanto, não adicione blocos de captura que não façam nada ou apenas repitam.
O bloco catch deve adicionar algum valor. Por exemplo, mensagem de saída para o usuário final ou erro de log.
Não use a exceção para a lógica normal do programa de fluxo. Por exemplo:
por exemplo, validação de entrada. <- Esta não é uma situação excepcional válida; você deve escrever um método
IsValid(myInput);
para verificar se o item de entrada é válido ou não.Código de design para evitar exceção. Por exemplo:
Se passarmos um valor que não pode ser analisado para int, esse método lançaria uma exceção, em vez disso, poderíamos escrever algo como isto:
bool TryParse(string input,out int result);
<- este método retornaria booleano indicando se a análise foi bem-sucedida.Talvez isso esteja um pouco fora do escopo desta questão, mas espero que isso ajude você a tomar decisões corretas quando se trata de
try {} catch(){}
exceções.fonte