Pergunta

Eu vi o seguinte código muitas vezes:

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);

    // or
    // throw;

    // or
    // throw ex;
}

Pode me explicar o propósito de re-lançar uma exceção? Trata-se de seguir a / melhor prática padrão no tratamento de exceção? (Eu tenho algum lugar leitura que é chamado de "Caller informar" padrão?)

Foi útil?

Solução

Relançando a mesma exceção é útil se você quiser, por exemplo, registrar a exceção, mas não lidar com isso.

Jogando uma nova exceção que envolve a exceção capturada é bom para a abstração. por exemplo, a biblioteca utiliza uma biblioteca de terceiros que lança uma exceção que os clientes de sua biblioteca não deve conhecer. Nesse caso, você envolvê-la em um tipo de exceção mais nativa para a sua biblioteca, e jogar que em vez.

Outras dicas

Na verdade, há uma diferença entre

throw new CustomException(ex);

e

throw;

O segundo irá preservar as informações de pilha.

Mas às vezes você quer fazer a exceção mais "amigável" para o seu domínio de aplicação, em vez de deixar o DatabaseException chegar ao seu GUI, você vai aumentar a sua exceção personalizada que contém a exceção original.

Por exemplo:

try
{

}
catch (SqlException ex)
{
    switch  (ex.Number) {
        case 17:
        case 4060:
        case 18456:
           throw new InvalidDatabaseConnectionException("The database does not exists or cannot be reached using the supplied connection settings.", ex);
        case 547:
            throw new CouldNotDeleteException("There is a another object still using this object, therefore it cannot be deleted.", ex);
        default:
            throw new UnexpectedDatabaseErrorException("There was an unexpected error from the database.", ex);
    } 
}

Às vezes você quer esconder os detalhes de implementação de um método ou melhorar o nível de abstração de um problema de modo que é mais significativo para o chamador de um método. Para fazer isso, você pode interceptar a exceção original e substituto uma exceção personalizada que é mais adequado para explicar o problema.

Tomemos por exemplo um método que carrega os detalhes do usuário solicitado de um arquivo de texto. O método assume que um arquivo de texto existe nomeado com o ID do usuário e um sufixo de “.data”. Quando esse arquivo não existe realmente, não faz muito sentido para lançar uma FileNotFoundException porque o fato de que os detalhes de cada usuário são armazenados em um arquivo de texto é um detalhe de implementação interna para o método. Portanto, este método poderia, em vez enrole a exceção original em uma exceção personalizada com uma mensagem explicativa.

Ao contrário do código que você está demonstrado, a melhor prática é que a exceção original deve ser mantido por carregá-lo como a propriedade InnerException da sua nova exceção. Isto significa que um desenvolvedor pode ainda analisar o problema subjacente, se necessário.

Quando você está criando uma exceção personalizada, aqui está uma lista de verificação útil:

• Encontrar um nome bom que transmite por que a exceção foi lançada e certifique-se que as extremidades de nome com a palavra “exceção”.

• Certifique-se de implementar os três construtores de exceção padrão.

• Certifique-se de marcar a sua exceção com o atributo Serializable.

• Certifique-se de implementar o construtor desserialização.

• Adicione quaisquer propriedades de exceção personalizada que desenvolvedores possam ajudar a compreender e lidar com a sua exceção melhor.

• Se você adicionar as propriedades personalizadas, certifique-se de que você implementar e substituir GetObjectData para serializar suas propriedades personalizadas.

• Se você adicionar as propriedades personalizadas, substituir a propriedade mensagem de modo que você pode adicionar suas propriedades para a mensagem de exceção padrão.

• Lembre-se de anexar a exceção original usando a propriedade InnerException de sua exceção personalizada.

Você normalmente pegar e re-lance para uma de duas razões, dependendo de onde o código fica arquitetura dentro de uma aplicação.

No núcleo de um aplicativo que normalmente captura e re-lance para traduzir uma exceção em algo mais significativo. Por exemplo, se você está escrevendo uma camada de acesso de dados e utilização de códigos de erro personalizadas com o SQL Server, você pode traduzir SqlException em coisas como ObjectNotFoundException. Isso é útil porque (a) torna-se mais fácil para os chamadores tipos alça específicas de exceção, e (b) porque impede detalhes de implementação dessa camada como o fato de que você está usando SQL Server para persistência vazando para outras camadas, que permite-lhe mudar as coisas no futuro com mais facilidade.

No limites de aplicações é comum captura e re-lance sem traduzir uma exceção para que você pode entrar detalhes do mesmo, auxiliando na depuração e diagnóstico de problemas ao vivo. Idealmente você quer publicar em algum erro que a equipe de operações pode facilmente monitorar (por exemplo, o registo de eventos), bem como em algum lugar que dá contexto em torno de onde a exceção aconteceu no fluxo de controle para desenvolvedores (tipicamente rastreamento).

Eu posso pensar das seguintes razões:

  • Mantendo o conjunto de tipos de exceção lançada fixos, como parte da API, para que os chamadores só tem que se preocupar com o conjunto fixo de exceções. Em Java, você está praticamente forçado a fazer isso, por causa do mecanismo de exceções verificadas.

  • Adicionando algumas informações de contexto para a exceção. Por exemplo, em vez de deixar o "registro não encontrado" bare passar através do DB, você pode querer pegá-lo e adicionar "... durante o processamento de ordem XXX, procurando YYY produto".

  • Fazer alguma limpeza -. Fechamento de arquivos, rolando transações de volta, liberando algumas alças

Geralmente o "Algo Do" quer envolve explicar melhor a exceção (por exemplo, envolvendo-o em outra exceção), ou informações de rastreamento através de uma determinada fonte.

Outra possibilidade é se o tipo de exceção não é informação suficiente para saber se um necessidades de exceção de ser pego, caso em que a captura-lo um exame que irá fornecer mais informações.

Isto não quer dizer que o método é usado para puramente boas razões, muitas vezes ele é usado quando um desenvolvedor pensa informações de rastreamento pode ser necessário em algum momento futuro, caso em que você começa try {} catch {throw;} estilo , que não é útil a todos.

Eu acho que depende do que você está tentando fazer com a exceção.

Um bom motivo seria a de registrar o erro em primeiro lugar na captura, e depois jogá-lo até a interface do usuário para gerar uma mensagem de erro amigável com a opção de ver uma visão mais "avançado / detalhado" do erro, que contém o erro original.

Outra abordagem é uma abordagem "nova tentativa", por exemplo, uma contagem de erro é mantido, e depois de uma certa quantidade de tentativas que a única vez que o erro é enviado até a pilha (isto é feito às vezes para acesso de banco de dados para chamadas de banco de dados que tempo limite, ou no acesso a serviços web mais redes lentas).

Haverá um monte de outras razões para fazê-lo embora.

FYI, esta é uma pergunta relacionada sobre cada tipo de re-lance: para lançar exceções

A minha pergunta incide sobre "Por que" nós re-throw exceções e seu uso na estratégia de tratamento de exceção do aplicativo.

Até que eu comecei usando o EntLib ExceptionBlock, eu estava usando-os para registrar erros antes de jogá-los. Tipo de desagradável quando você acha que eu poderia ter lidado com eles naquele momento, mas no momento em que era melhor tê-los falhar nastily em UAT (após log-los) ao invés de cobertura de um fluxo-on bug.

O aplicativo provavelmente vai estar pegando essas exceções jogado re-mais acima na pilha de chamadas e assim re-jogá-los permite que mais acima manipulador para interceptar e processá-las conforme apropriado. É bastante comum para aplicação de ter um manipulador de exceção de nível superior que registros ou relata os expections.

Outra alternativa é que o codificador era preguiçoso e, em vez de pegar apenas o conjunto de exceções que eles querem lidar com eles pegaram tudo e, em seguida, re-lançado apenas os que realmente não pode manipular.

Como Rafal mencionado, por vezes, isso é feito para converter uma exceção verificada para uma exceção não verificada, ou para uma exceção verificada que é mais adequado para uma API. Há um exemplo aqui:

http://radio-weblogs.com/0122027 /stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html

Se você olhar para exceções como em uma maneira alternativa para obter um resultado método , em seguida, re-lançar uma exceção é como embrulhar seu resultado em algum outro objeto.

E esta é uma operação comum em um mundo não-excepcional. Geralmente isso acontece em uma beira de duas camadas de aplicação - quando uma função de B camada chama uma função de C camada, ele transforma o resultado de C em forma interna do B.

A -- calls --> B -- calls --> C

Se isso não acontecer, então no A camada que chama a B camada haverá um conjunto completo de exceções do JDK de manusear. : -)

Como também a resposta aceita aponta, camada A pode até não estar ciente de exceção de C.

Exemplo

Layer A , servlet: recupera uma imagem e é meta informação
Layer B , biblioteca JPEG: recolhe etiquetas DCIM disponíveis para analisar um arquivo JPEG
Layer C , um DB simples: uma leitura classe registros de cordas de um arquivo de acesso aleatório. Alguns bytes estão quebrados, por isso lança uma exceção dizendo que "não pode ler string UTF-8 para gravar 'bibliographicCitation'".

Assim A não vai entender o significado de 'bibliographicCitation'. Então B deve traduzir essa exceção para A em TagsReadingException que envolve o original.

A principal razão de exceções jogando re-é deixar Call Stack intocada, para que possa obter imagem mais completa do que acontece e chama sequência.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top