Encontrei um código como este em um de nossos projetos:
SomeClass QueryServer(string args)
{
try
{
return SomeClass.Parse(_server.Query(args));
}
catch (Exception)
{
return null;
}
}
Tanto quanto eu entendo, suprimir erros como esse é uma prática ruim, pois destrói informações úteis da exceção do servidor original e faz com que o código continue quando realmente deve terminar.
Quando é apropriado suprimir completamente todos os erros como este?
Respostas:
Imagine o código com milhares de arquivos usando várias bibliotecas. Imagine que todos eles são codificados assim.
Imagine, por exemplo, que uma atualização do seu servidor faça com que um arquivo de configuração desapareça; e agora tudo o que você tem é um rastreamento de pilha é uma exceção de ponteiro nulo ao tentar usar essa classe: como você resolveria isso? Pode levar horas, onde pelo menos apenas o registro do rastreamento de pilha bruta do arquivo não encontrado [caminho do arquivo] pode permitir que você resolva momentaneamente.
Ou ainda pior: uma falha em uma das bibliotecas que você usa após uma atualização que faz com que seu código falhe mais tarde. Como você pode rastrear isso de volta à biblioteca?
Mesmo sem um tratamento robusto de erros, apenas fazendo
ou
pode economizar horas de tempo.
Mas há alguns casos em que você pode realmente querer ignorar a exceção e retornar nulo como este (ou não fazer nada). Especialmente se você se integrar a algum código legado mal projetado e essa exceção for esperada como um caso normal.
De fato, ao fazer isso, você deve se perguntar se realmente está ignorando a exceção ou "lidando adequadamente" com suas necessidades. Se retornar null estiver "manipulando adequadamente" sua exceção nesse caso, faça-o. E adicione um comentário por que isso é a coisa certa a se fazer.
As melhores práticas são coisas a serem seguidas na maioria dos casos, talvez 80%, talvez 99%, mas você sempre encontrará um caso de ponta em que elas não se aplicam. Nesse caso, deixe um comentário por que você não está seguindo a prática para os outros (ou mesmo para você) que lerão seu código meses depois.
fonte
Há casos em que esse padrão é útil - mas eles geralmente são usados quando a exceção gerada nunca deveria ter sido (ou seja, quando as exceções são usadas para o comportamento normal).
Por exemplo, imagine que você tenha uma classe que abre um arquivo, armazena-o no objeto retornado. Se o arquivo não existir, considere que não é um caso de erro, você retorna nulo e permite que o usuário crie um novo arquivo. O método de abertura de arquivo pode lançar uma exceção aqui para indicar a ausência de arquivo, portanto, capturá-lo silenciosamente pode ser uma situação válida.
No entanto, não é muito comum você querer fazer isso e, se o código estiver repleto de um padrão desses, você deve lidar com isso agora. No mínimo, eu esperaria que uma captura tão silenciosa escrevesse uma linha de log dizendo que foi isso que aconteceu (você tem rastreamento, certo) e um comentário para explicar esse comportamento.
fonte
null
se esse for o melhor que o idioma escolhido pode oferecer.Isso é 100% dependente do contexto. Se o chamador dessa função tiver o requisito de exibir ou registrar erros em algum lugar, é óbvio que isso não faz sentido. Se o chamador ignorasse todos os erros ou mensagens de exceção, não faria muito sentido retornar a exceção ou sua mensagem. Quando o requisito é fazer com que o código termine apenas sem exibir nenhum motivo, uma chamada
provavelmente seria suficiente. Você deve considerar se isso tornará difícil encontrar a causa do erro pelo usuário - se for esse o caso, essa é a forma incorreta de tratamento de erros. No entanto, se o código de chamada se parecer com isso
você deve discutir com o desenvolvedor durante a revisão do código. Se alguém implementa essa forma de tratamento sem erros apenas porque é muito preguiçoso para descobrir os requisitos corretos, esse código não deve entrar em produção e o autor do código deve ser ensinado sobre as possíveis conseqüências dessa preguiça. .
fonte
Nos anos que passei programando e desenvolvendo sistemas, há apenas duas situações em que achei o padrão em questão útil (em ambos os casos, a supressão continha também o registro da exceção lançada, não considero captura e
null
retorno simples como uma boa prática. )As duas situações são as seguintes:
1. Quando a exceção não foi considerada um estado excepcional
É quando você executa uma operação em alguns dados, que podem ser lançados, você sabe que podem ser lançados, mas você ainda deseja que seu aplicativo continue em execução, porque você não precisa dos dados processados. Se você os receber, é bom; se não, também é bom.
Alguns atributos opcionais de uma classe podem ser lembrados.
2. Ao fornecer uma nova (melhor, mais rápida?) Implementação de uma biblioteca usando uma interface já usada em um aplicativo
Imagine que você tenha um aplicativo usando algum tipo de biblioteca antiga, que não emitiu exceções, mas retornou
null
por erro. Então, você criou um adaptador para esta biblioteca, copiando praticamente a API original da biblioteca e está usando essa nova interface (ainda não lançada) em seu aplicativo e manipulando asnull
verificações por conta própria.É apresentada uma nova versão da biblioteca, ou talvez uma biblioteca completamente diferente, oferecendo a mesma funcionalidade que, em vez de retornar
null
s, gera exceções e você deseja usá-la.Você não deseja vazar as exceções para o aplicativo principal, portanto, suprimi-las e registrá-las no adaptador criado para quebrar essa nova dependência.
O primeiro caso não é um problema, é o comportamento desejado do código. Na segunda situação, no entanto, se em todo lugar o
null
valor de retorno do adaptador da biblioteca realmente significa um erro, refatorar a API para gerar uma exceção e capturá-la em vez de procurarnull
pode ser (e geralmente é em termos de código) uma boa idéia.Pessoalmente, uso a supressão de exceção apenas no primeiro caso. Eu o usei apenas para o segundo caso, quando não tínhamos orçamento para fazer o restante do aplicativo funcionar com as exceções em vez de
null
s.fonte
Embora pareça lógico dizer que os programas só devem capturar exceções que eles sabem como manipular e não podem saber como lidar com exceções que não foram antecipadas pelo programador, essa afirmação ignora o fato de que muitas operações podem falhar em um ambiente. número quase ilimitado de maneiras que não têm efeitos colaterais e que, em muitos casos, o manuseio adequado para a grande maioria dessas falhas será idêntico; os detalhes exatos da falha serão irrelevantes e, consequentemente, não importa se o programador os antecipou.
Se, por exemplo, o objetivo de uma função é ler um arquivo de documento em um objeto adequado e criar uma nova janela de documento para mostrar esse objeto ou relatar ao usuário que o arquivo não pôde ser lido, uma tentativa de carregar um arquivo arquivo de documento inválido não deve travar o aplicativo - ele deve mostrar uma mensagem indicando o problema, mas deixar o restante do aplicativo continuar sendo executado normalmente, a menos que, por algum motivo, a tentativa de carregar o documento tenha corrompido o estado de outra coisa no sistema .
Fundamentalmente, o tratamento adequado de uma exceção geralmente depende menos do tipo de exceção do que do local em que foi lançada; se um recurso é protegido por um bloqueio de leitura e gravação e uma exceção é lançada dentro de um método que adquiriu o bloqueio para leitura, o comportamento adequado geralmente deve ser liberar o bloqueio, pois o método não pode ter feito nada com o recurso . Se uma exceção for lançada enquanto o bloqueio for adquirido para gravação, o bloqueio deve ser frequentemente invalidado, pois o recurso protegido pode estar em um estado inválido; se a primitiva de bloqueio não tiver um estado "inválido", adicione um sinalizador para rastrear essa invalidação. Liberar o bloqueio sem invalidá-lo é ruim porque outro código pode ver o objeto protegido em um estado inválido. Deixar a trava pendurada, no entanto, não é ' uma solução adequada também. A solução certa é invalidar o bloqueio para que quaisquer tentativas pendentes ou futuras de aquisição falhem imediatamente.
Se um recurso inválido for abandonado antes de tentar utilizá-lo, não há motivo para interromper o aplicativo. Se um recurso invalidado for crítico para a operação continuada de um aplicativo, o aplicativo precisará ser desativado, mas a invalidação do recurso provavelmente fará com que isso aconteça. O código que recebeu a exceção original geralmente não tem como saber qual situação se aplica, mas se invalida o recurso, pode garantir que a ação correta acabe sendo executada nos dois casos.
fonte
A ingestão desse tipo de erro não é particularmente útil para ninguém. Sim, o chamador original pode não se importar com a exceção, mas outra pessoa pode .
Então o que eles fazem? Eles adicionam código para lidar com a exceção e ver qual sabor de exceção está recebendo de volta. Ótimo. Mas se o manipulador de exceções for deixado em, o chamador original não será mais retornado nulo e algo será interrompido em outro lugar.
Mesmo se você estiver ciente de que algum código upstream pode gerar um erro e, assim, retornar nulo, seria seriamente inerte da sua parte não tentar pelo menos evitar a exceção no código de chamada IMHO.
fonte
Eu já vi exemplos em que bibliotecas de terceiros tinham métodos potencialmente úteis, exceto que lançam exceções em alguns casos que devem funcionar para mim. O que eu posso fazer?
Por exemplo, o método da biblioteca
retorna o primeiro foo, mas se não houver, ele lança uma exceção. Mas o que eu preciso é de um método para retornar o primeiro foo ou um nulo. Então eu escrevo este:
Não é legal. Mas às vezes a solução pragmática.
fonte