Uso eficiente do bloco try / catch?

21

Os blocos catch devem ser usados ​​para escrever lógica, por exemplo, controlar o controle de fluxo etc. Ou apenas por lançar exceções? Isso afeta a eficiência ou a manutenção do código?

Quais são os efeitos colaterais (se houver) da escrita lógica no bloco catch?

EDITAR:

Eu vi uma classe Java SDK na qual eles escreveram lógica dentro do bloco catch. Por exemplo (trecho retirado da java.lang.Integerclasse):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

EDIT2 :

Eu estava passando por um tutorial em que eles contam como uma vantagem de escrever a lógica de casos excepcionais dentro das exceções:

As exceções permitem que você escreva o fluxo principal do seu código e lide com casos excepcionais em outros lugares.

Alguma orientação específica quando escrever lógica no bloco catch e quando não?

HashimR
fonte
12
@ Codificador Eu acho que você generaliza demais um pouco. Especialmente porque com muitas das APIs existentes, você não pode evitá-lo.
código é o seguinte
8
@ Codificador: em Java, as exceções são frequentemente usadas como um mecanismo para sinalizar problemas que fazem parte do fluxo de código legítimo. Por exemplo, se você tentar abrir um arquivo e ele falhar, a biblioteca Java informará isso lançando uma exceção, porque as APIs que levam à abertura de um arquivo não têm outro mecanismo para retornar um erro.
precisa saber é o seguinte
4
@Code Depends. Acho que ao fazer IO ou ao analisar dados complexos, que quase sempre são formatados corretamente, lançando uma exceção para significar um erro torna o código muito mais limpo.
código é o seguinte
1
@ Codificado Sim, a execução está aí para o método dizer que não foi possível fazer o que foi solicitado. Mas as exceções não devem ser usadas para controle de fluxo, e é por isso que o C # também possui um método int.TryParse que não gera.
21712 Andy
1
@ Codificador: Isso é lixo total. Coisas excepcionais em um nível de código podem não ser excepcionais em outro - ou você pode, por exemplo, apresentar ou adicionar informações de erro ou depuração antes de continuar.
DeadMG

Respostas:

46

O exemplo que você cita é devido ao mau design da API (não há uma maneira limpa de verificar se uma String é um número inteiro válido, exceto tentando analisá-la e capturar a exceção).

No nível técnico, throw e try / catch são construções de fluxo de controle que permitem aumentar a pilha de chamadas, nada mais e nada menos. O salto na pilha de chamadas conecta implicitamente o código que não está muito próximo na fonte, o que é ruim para a manutenção . Portanto, ele só deve ser usado quando você precisar fazer isso e as alternativas são ainda piores. O caso amplamente aceito em que as alternativas são piores é o tratamento de erros (códigos de retorno especiais que precisam ser verificados e transmitidos manualmente para cada nível da pilha de chamadas).

Se você tem um caso em que as alternativas são piores (e você realmente considerou todas elas com cuidado), então eu diria que usar o throw e try / catch para o fluxo de controle é bom. Dogma não é um bom substituto para o julgamento.

Michael Borgwardt
fonte
1
+1, bons pontos conquistados. Eu acho que algum processamento pode ser válido na captura, como a preparação de uma mensagem de erro e o log de erros. A liberação de recursos caros, como conexões db e referências (.NET) COM, pode ser adicionada ao bloco Finalmente.
precisa saber é o seguinte
@EmmadKareem Pensei em liberar recursos, mas geralmente você precisa liberá-los de qualquer maneira em algum momento, mesmo sem situação de erro. Assim, você pode se repetir. A preparação de uma mensagem de log é obviamente aceitável.
scarfridge
@ MichaelBorgwardt Acho que o design da API não é tão ruim (embora possa ser melhor). Tentar analisar uma cadeia inválida é claramente uma condição de erro e, portanto, exatamente o "caso amplamente aceito" que você mencionou. Concordado, um método para determinar se uma string é um número sintaticamente correto é útil (é aqui que poderia ser melhor). No entanto, forçar todo mundo a chamar esse método antes da análise real não é possível e precisamos lidar com o erro. A mistura do resultado real e do código de erro é desconfortável e você ainda lançaria a exceção. Mas talvez uma exceção de tempo de execução.
scarfridge
3
+1 para a última frase.
FrustratedWithFormsDesigner
2
@scarfridge: Eu concordo totalmente com você :) A ausência de um método retornando boolean isInteger (String) é tudo o que eu quis dizer com "má concepção API"
Michael Borgwardt
9

Isso é algo que tende a depender da linguagem e do paradigma.

A maior parte do meu trabalho é feita em Java (e às vezes em C ++). A tendência é usar exceções apenas para condições excepcionais. Há algumas perguntas no Stack Overflow sobre a sobrecarga de desempenho das exceções em Java e, como você pode ver, não é exatamente insignificante. Há também outras preocupações, como a legibilidade e a manutenção do código. Quando usadas corretamente, as exceções podem delegar a manipulação de erros nos componentes apropriados.

No entanto, em Python, a idéia de que é mais fácil pedir perdão do que a permissão reina. Na comunidade Python, isso é conhecido como EAFP , que é contrastado com a abordagem "olhar antes de pular" ( LBYL ) em linguagens no estilo C.

Thomas Owens
fonte
2
+1 por mencionar a sobrecarga com exceções. Eu faço o .NET e (pelo menos na minha máquina) até sinto quando uma exceção está prestes a acontecer pela duração que leva em alguns casos, em comparação com outras operações normais.
NoChance
2
@EmmadKareem Somente se você tiver um depurador conectado. Você pode lançar vários milhares deles por segundo sem um depurador.
código é o seguinte
@CodeInChaos, você está correto. Como eu poderia saber, eu escrevo um código tal limpa que nunca se qualquer em tempo de execução :)
NoChance
@EmmadKareem Dependendo de como se escreve um aplicativo de rede, as falhas de conexão ou protocolo são tratadas com uma exceção. As exceções precisam ser razoavelmente rápidas em um aplicativo para evitar ataques triviais de DoS.
código é o seguinte
@CodeInChaos, é bom saber. Eu estava brincando no meu último comentário.
precisa saber é o seguinte
9

Essa não é a melhor maneira de pensar sobre blocos try / catch. A maneira de pensar sobre os blocos try / catch é: ocorreu uma falha, onde é o melhor lugar para lidar com ESTA falha específica. Essa pode ser a próxima linha de código, pode haver vinte níveis na cadeia de chamadas. Onde quer que seja, é onde deveria estar.

O que o bloco catch faz depende do erro e o que você pode fazer sobre isso. Às vezes, ele pode ser simplesmente ignorado (falha em excluir um arquivo de rascunho sem dados imortantes), outras vezes, é usado para definir um valor de retorno de funções como true ou false (método de análise int do java); às vezes, você sai do programa . Tudo isso depende do erro.

A parte importante a entender é: catch block == Eu sei como lidar com isso.

jmoreno
fonte
6

Os blocos de captura devem ser usados ​​apenas para manipular exceções, nada mais e nunca para o controle de fluxo. Em qualquer situação em que você queira usar exceções para gerenciar o fluxo, é melhor verificar as pré-condições.

O tratamento do fluxo com exceções é lento e semanticamente incorreto.

Tom Squires
fonte
4
Eu sei o que você está dizendo, mas vale a pena notar que tudo o que as exceções fazem é lidar com o fluxo do programa - apenas que ele deve ser usado apenas para controlar o fluxo de erros excepcional ... #
Max
Não há diretrizes específicas quando escrever lógica no bloco catch e quando não?
HashimR
4
Os analisadores de números e formatadores de Java são exemplos famosos de uso problemático de exceções: a única maneira razoável de verificar uma pré-condição (que uma sequência representa um número analisável) é tentar analisá-la e, se ela falhar com uma exceção, quais são suas escolhas na prática? Você precisa capturar a exceção e gerenciar o fluxo com ela, independentemente de ser considerado semanticamente incorreto.
Joonas Pulakka
@JoonasPulakka Eu acho que o seu comentário wrt analisadores Número e formatadores de texto qualifica como uma resposta de tamanho completo a esta pergunta
mosquito
5

Os blocos catch devem ser usados ​​para escrever lógica, por exemplo, controlar o controle de fluxo etc. Ou apenas por lançar exceções? Isso afeta a eficiência ou a manutenção do código?

Primeiro, esqueça a noção de que "as exceções devem ser usadas para condições excepcionais". Além disso, pare de se preocupar com a eficiência, até ter um código com desempenho inaceitável e medir para saber onde está o problema.

O código é mais fácil de entender quando as ações seguem em uma sequência simples, sem condicionais. As exceções aprimoram a capacidade de manutenção removendo a verificação de erros do fluxo normal. Não importa se a execução segue o fluxo normal 99,9% das vezes ou 50% das vezes ou 20% das vezes.

Lance uma exceção quando uma função não puder retornar um valor, quando um procedimento não puder concluir a ação esperada ou quando um construtor não puder produzir um objeto utilizável. Isso permite aos programadores assumir que uma função sempre retorna um resultado utilizável, um procedimento sempre conclui a ação solicitada, um objeto construído está sempre em um estado utilizável.

O maior problema que vejo no código de manipulação de exceções é que os programadores escrevem blocos try / catch quando não deveriam. Por exemplo, na maioria dos aplicativos da Web, se uma solicitação de banco de dados lança uma exceção, não há nada que possa ser feito no controlador. Uma cláusula de captura genérica no nível mais alto é suficiente. Em seguida, o código do controlador pode ignorar alegremente a possibilidade de o disco ter travado ou o banco de dados estar offline ou qualquer outra coisa.

Kevin Cline
fonte
1

Blocos de captura não devem ser usados ​​para escrever lógica de código. Eles devem ser usados ​​apenas para lidar com erros. Um exemplo é (1) limpar todos os recursos alocados, (2) imprimir uma mensagem útil e (3) sair normalmente.

É verdade que as exceções podem ser abusadas e causar gotoefeitos indesejados . Mas isso não os torna inúteis. Quando usados ​​corretamente , eles podem melhorar muitos aspectos do seu código.

sakisk
fonte