Eu tenho lido os conselhos sobre esta questão sobre como uma exceção deve ser tratada o mais próximo possível de onde é levantada.
Meu dilema sobre a melhor prática é se deve-se usar um try / catch / finalmente para retornar uma enumeração (ou um int que represente um valor, 0 para erro, 1 para ok, 2 para aviso etc., dependendo do caso), para que uma resposta está sempre em ordem ou deve-se deixar passar a exceção para que a parte que está chamando lide com ela?
Pelo que pude entender, isso pode ser diferente dependendo do caso, então o conselho original parece estranho.
Por exemplo, em um serviço da web, você sempre desejaria retornar um estado, é claro, portanto, qualquer exceção deve ser tratada no local, mas digamos que dentro de uma função que publique / obtenha alguns dados via http, você deseje o exceção (por exemplo, no caso de 404), apenas passar para o que a acionou. Caso contrário, seria necessário criar uma maneira de informar a parte que chama da qualidade do resultado (erro: 404), bem como o próprio resultado.
Embora seja possível tentar capturar a exceção 404 dentro da função auxiliar que obtém / publica os dados, você deveria? É apenas eu que uso um smallint para denotar estados no programa (e documentá-los adequadamente, é claro) e depois utilizo essas informações para fins de validação de sanidade (tudo ok / tratamento de erros) fora?
Atualização: Eu esperava uma exceção fatal / não fatal para a classificação principal, mas não quis incluí-la para não prejudicar as respostas. Deixe-me esclarecer sobre o que é a pergunta: Manipulando as exceções lançadas, não lançando exceções. Qual é o efeito desejado: detecte um erro e tente se recuperar dele. Se a recuperação não for possível, forneça o feedback mais significativo.
Novamente, com o exemplo http get / post, a pergunta é: você deve fornecer um novo objeto que descreva o que aconteceu com o chamador original? Se esse auxiliar estivesse em uma biblioteca que você está usando, esperaria que ele fornecesse um código de status para a operação ou o incluiria em um bloco try-catch? Se você o estiver projetando , você forneceria um código de status ou lançaria uma exceção e deixaria o nível superior convertê-lo em um código / mensagem de status?
Sinopse: Como você escolhe se um pedaço de código, em vez de produzir uma exceção, retorna um código de status junto com os resultados que ele pode produzir?
fonte
Respostas:
Exceções devem ser usadas para condições excepcionais. Lançar uma exceção é basicamente fazer a declaração: "Não posso lidar com essa condição aqui; alguém mais acima na pilha de chamadas pode capturar isso para mim e lidar com isso?"
O retorno de um valor pode ser preferível, se estiver claro que o chamador aceitará esse valor e fará algo significativo com ele. Isso é especialmente verdadeiro se o lançamento de uma exceção tem implicações no desempenho, ou seja, pode ocorrer em um loop restrito. Lançar uma exceção leva muito mais tempo do que retornar um valor (em pelo menos duas ordens de magnitude).
Exceções nunca devem ser usadas para implementar a lógica do programa. Em outras palavras, não lance uma exceção para fazer algo; lançar uma exceção para declarar que não poderia ser feito.
fonte
Alguns bons conselhos que li uma vez foram: lançar exceções quando você não puder progredir, devido ao estado dos dados com os quais está lidando; no entanto, se você tiver um método que possa gerar uma exceção, forneça sempre que possível um método para afirmar se os dados são realmente válido antes que o método seja chamado.
Por exemplo, System.IO.File.OpenRead () lançará um FileNotFoundException se o arquivo fornecido não existir, mas também fornece um método .Exists () que retorna um valor booleano indicando se o arquivo está presente, que você deve chamar antes chamando OpenRead () para evitar exceções inesperadas.
Para responder à parte "quando devo lidar com uma exceção" da pergunta, eu diria onde quer que você possa realmente fazer algo a respeito. Se o seu método não puder lidar com uma exceção lançada por um método chamado, não a pegue. Deixe que ele suba mais alto na cadeia de chamadas para algo que possa lidar com isso. Em alguns casos, pode ser apenas um logger ouvindo Application.UnhandledException.
fonte
Esse é um design terrível. Não "mascare" uma exceção traduzindo para um código numérico. Deixe isso como uma exceção apropriada e inequívoca.
É para isso que servem as exceções.
Não é uma verdade universal . É uma boa ideia algumas vezes. Outras vezes, não é tão útil.
fonte
if
instruções complicadas em vez de (às vezes) um tratamento de exceção mais simples. A resposta ("Como você escolhe ...") é simples. Não . Eu acho que dizer isso contribui para a conversa. Você é livre para ignorar esta resposta, no entanto.Eu concordo com S.Lott . Capturar a exceção o mais próximo possível da fonte pode ser uma boa ou má idéia, dependendo da situação. A chave para lidar com exceções é capturá-las apenas quando você pode fazer algo sobre isso. Pegá-los e retornar um valor numérico para a função de chamada geralmente é um design ruim. Você só quer deixá-los flutuar até que você possa se recuperar.
Eu sempre considero o tratamento de exceções um passo à frente da minha lógica de aplicativo. Pergunto-me: se essa exceção for lançada, quanto tempo de backup da pilha de chamadas devo rastrear antes que meu aplicativo esteja em um estado recuperável? Em muitos casos, se não há nada que eu possa fazer no aplicativo para recuperar, isso pode significar que eu não o pego até o nível superior e apenas registro a exceção, falho no trabalho e tento desligar de maneira limpa.
Realmente não existe uma regra rígida e rápida sobre quando e como configurar o tratamento de exceções, exceto deixá-los em paz até que você saiba o que fazer com eles.
fonte
Exceções são coisas bonitas. Eles permitem que você produza uma descrição clara de um problema de tempo de execução sem recorrer a ambigüidades desnecessárias. Exceções podem ser digitadas, subtipadas e podem ser tratadas por tipo. Eles podem ser repassados para manipulação em outros lugares e, se não puderem ser manipulados, podem ser re-levantados para serem tratados em uma camada mais alta no seu aplicativo. Eles também retornarão automaticamente do seu método sem a necessidade de invocar muita lógica maluca para lidar com códigos de erro ofuscados.
Você deve lançar uma exceção imediatamente após encontrar dados inválidos no seu código. Você deve agrupar as chamadas para outros métodos em uma tentativa .. captura .. finalmente para lidar com quaisquer exceções que possam ser lançadas e, se você não souber como responder a uma exceção, jogue-a novamente para indicar as camadas mais altas que há algo errado que deve ser tratado em outro lugar.
Gerenciar códigos de erro pode ser muito difícil. Você geralmente acaba com muitas duplicações desnecessárias no seu código e / ou muita lógica bagunçada para lidar com estados de erro. Ser um usuário e encontrar um código de erro é ainda pior, pois o código em si não será significativo e não fornecerá ao usuário um contexto para o erro. Uma exceção, por outro lado, pode dizer ao usuário algo útil, como "Você esqueceu de inserir um valor" ou "você inseriu um valor inválido, eis o intervalo válido que você pode usar ..." ou "Eu não Para saber o que aconteceu, entre em contato com o suporte técnico e diga que eu acabei de travar e forneça o seguinte rastreamento de pilha ... ".
Portanto, minha pergunta ao OP é por que diabos você NÃO deseja usar exceções ao retornar códigos de erro?
fonte
Todas boas respostas. Gostaria também de acrescentar que o retorno de um código de erro em vez de lançar uma exceção pode tornar o código do chamador mais complicado. Diga o método A chama o método B chama o método C e C encontra um erro. Se C retornar um código de erro, agora B precisará ter lógica para determinar se pode lidar com esse código de erro. Se não puder, será necessário retorná-lo para A. Se A não puder lidar com o erro, o que você fará? Lançar uma exceção? Neste exemplo, o código é muito mais limpo se C simplesmente lança uma exceção, B não captura a exceção e, portanto, aborta automaticamente sem nenhum código extra necessário para fazê-lo, e A pode capturar certos tipos de exceção e permitir que outros continuem a chamada pilha.
Isso traz à mente uma boa regra para codificar:
fonte
Usei uma combinação das duas soluções: para cada função de validação, passo um registro que preenchei com o status de validação (um código de erro). No final da função, se houver um erro de validação, lancei uma exceção, desta forma, não ligo uma exceção para cada campo, mas apenas uma vez.
Eu também aproveitei que o lançamento de uma exceção interromperia a execução porque não quero que a execução continue quando os dados forem inválidos.
Por exemplo
Se depender apenas de booleano, o desenvolvedor que usa minha função deve levar isso em consideração ao escrever:
mas ele pode se esquecer e apenas ligar
Validate()
(eu sei que ele não deveria, mas talvez ele possa).Então, no código acima, ganhei as duas vantagens:
fonte
Não há uma resposta aqui - como se não houvesse um tipo de HttpException.
Faz muito sentido que as bibliotecas HTTP subjacentes gerem uma exceção quando obtêm uma resposta 4xx ou 5xx; da última vez que olhei para as especificações HTTP, esses eram erros.
Quanto a lançar essa exceção - ou envolvê-la e rever novamente - acho que realmente é uma questão de caso de uso. Por exemplo, se você estiver escrevendo um wrapper para capturar alguns dados da API e expô-los a aplicativos, poderá decidir que semanticamente uma solicitação de um recurso inexistente que retorne um HTTP 404 faria mais sentido capturar isso e retornar nulo. Por outro lado, um erro 406 (não aceitável) pode valer a pena lançar um erro, pois isso significa que algo mudou e o aplicativo deve estar travando, queimando e gritando por ajuda.
fonte