Acabei de descobrir um código adorável em nosso aplicativo de empresas que usa blocos Try-Catch como operadores lógicos.
Ou seja, "faça algum código, se isso gerar esse erro, faça esse código, mas se isso gerar esse erro, faça essa terceira coisa".
Ele usa "Finalmente" como a declaração "else" que aparece.
Eu sei que isso está errado por natureza, mas antes de começar uma briga, eu esperava alguns argumentos bem pensados.
E ei, se você tiver argumentos para o uso do Try-Catch dessa maneira, informe.
Para quem está se perguntando, o idioma é C # e o código em questão tem mais de 30 linhas e está procurando exceções específicas, não está tratando TODAS as exceções.
error-handling
programming-logic
James P. Wright
fonte
fonte
try
'd. Nem todo caso excepcional que justifique uma exceção em geral deve ser fatal neste caso específico. Então, você poderia fazer isso de uma maneira mais simples, igual ou mais robusta sem usar exceções?Respostas:
O tratamento de exceções tende a ser uma maneira cara de lidar com o controle de fluxo (certamente para C # e Java).
O tempo de execução faz bastante trabalho quando um objeto de exceção é construído - reunindo o rastreamento da pilha, descobrindo onde a exceção é tratada e muito mais.
Tudo isso custa em recursos de memória e CPU que não precisam ser expandidos se instruções de controle de fluxo forem usadas para controle de fluxo.
Além disso, há um problema semântico. Exceções são para situações excepcionais, não para controle de fluxo normal. Deve-se usar o tratamento de exceções para lidar com situações imprevistas / excepcionais, não como o fluxo normal do programa, pois, caso contrário, uma exceção não capturada informará muito menos.
Além desses dois, há o problema de outras pessoas lerem o código. Usar exceções dessa maneira não é algo que a maioria dos programadores esperará, portanto, a legibilidade e o quão compreensível seu código é afetado. Quando alguém vê "Exceção", pensa - algo ruim aconteceu, algo que não deveria acontecer normalmente. Portanto, usar exceções dessa maneira é simplesmente confuso.
fonte
Como você sabe disso? Desisti de todo esse tipo de "conhecimento" e agora acredito que o código mais simples é o melhor. Suponha que você deseje converter uma cadeia de caracteres em um Opcional que esteja vazio se a análise falhar. Não há nada errado com:
Discordo completamente da interpretação usual de "Exceções são para condições excepcionais". Quando uma função não pode retornar um valor utilizável ou um método não pode atender às suas pós-condições, lance uma exceção. Não importa com que frequência essas exceções são lançadas até que haja um problema de desempenho demonstrado.
As exceções simplificam o código, permitindo a separação do tratamento de erros do fluxo normal. Basta escrever o código mais simples possível e, se for mais fácil usar o try-catch ou lançar uma exceção, faça isso.
Exceções simplificam o teste, reduzindo o número de caminhos através do código. Uma função sem ramificações será concluída ou lançará uma exceção. Uma função com várias
if
instruções para verificar códigos de erro possui muitos caminhos possíveis. É muito fácil errar uma das condições ou esquecê-las completamente, para que alguma condição de erro seja ignorada.fonte
int i; if(long.TryParse(s, out i)) return i; else return null;
. TryParse retorna false se a sequência não puder ser analisada. Se puder ser analisado, ele define o argumento de saída com os resultados da análise e retorna true. Nenhuma exceção ocorre (mesmo internamente no TryParse) e, especialmente, nenhuma exceção do tipo que significa erro de programador, como o .NETsFormatException
sempre indica.O trabalho de depuração e manutenção é muito difícil quando o fluxo de controle é executado usando exceções.
Inerentemente, as exceções são projetadas para serem um mecanismo para alterar o fluxo de controle normal do seu programa - de realizar atividades incomuns, causando efeitos colaterais incomuns, como uma maneira de sair de um vínculo particularmente rígido que não pode ser tratado com menos complicações. significa. Exceções são excepcionais. Isso significa que, dependendo do ambiente em que você está trabalhando, o uso de exceção no fluxo de controle regular pode causar:
Ineficiência Os obstáculos adicionais pelos quais o ambiente precisa passar para executar com segurança as mudanças de contexto relativamente difíceis necessárias para exceções requerem instrumentação e recursos.
Dificuldades de depuração Às vezes, informações úteis (ao tentar depurar um programa) são lançadas pela janela quando ocorrem exceções. Você pode perder o controle do estado ou histórico do programa relevante para entender o comportamento em tempo de execução
Problemas de manutenção O fluxo de execução é difícil de seguir através de saltos de exceção. Além disso, uma exceção pode ser lançada de dentro do código do tipo caixa preta, que pode não se comportar de maneiras fáceis de entender ao lançar uma exceção.
Más decisões de projeto Os programas criados dessa maneira incentivam um estado de espírito que, na maioria dos casos, não é facilmente mapeado para a solução de problemas com elegância. A complexidade do programa final desencoraja o programador de entender completamente sua execução e incentiva a tomada de decisões que levam a melhorias de curto prazo com altos custos de longo prazo.
fonte
Eu já vi esse padrão usado várias vezes.
Existem 2 problemas principais:
goto
. Os saltos são considerados prejudiciais, por várias razões. Eles devem ser usados, se todas as alternativas tiverem desvantagens consideráveis. De fato, em todo o meu código, lembro apenas de 2 casos, onde os saltos eram claramente a melhor solução.fonte
Às vezes, as exceções são mais rápidas. Vi casos em que as exceções de objetos nulos eram mais rápidas, mesmo em Java, do que o uso de estruturas de controle (não posso citar o estudo no momento, então você terá que confiar em mim). O problema surge quando o Java precisa levar algum tempo e preencher o rastreamento de pilha de uma classe de exceção personalizada em vez de usar as nativas (que parecem estar pelo menos parcialmente em cache). Antes de dizer que algo é unilateralmente mais rápido ou mais lento, seria bom fazer um benchmark.
No Python, não é apenas mais rápido, mas é muito mais correto fazer algo que possa causar uma exceção e depois manipular o erro. Sim, você pode aplicar um sistema de tipos, mas isso contraria a filosofia da linguagem - em vez disso, você deve simplesmente tentar chamar o método e obter o resultado! (Testar se um arquivo é gravável é semelhante - tente gravá-lo e pegue o erro).
Eu já vi momentos em que é mais rápido fazer algo estúpido como tabelas de consulta que não existiam do que descobrir se existe uma tabela no PHP + MySQL (a questão em que eu comparo essa é na verdade minha única resposta aceita com votos negativos) .
Tudo isso dito, o uso de exceções deve ser limitado por vários motivos:
try...catch
adeptos do fluxo de controle geralmente (na minha experiência) não seguem a filosofia "minimizar código no bloco de tentativa".else if
bloco. Já disse o suficiente (e se alguém responder com um "Mas eles poderiam usar classes de exceções diferentes", minha resposta é "Vá para o seu quarto e não saia até que você tenha pensado no que disse").Exception
, para o resto do mundo (como não aderentes àtry...catch
filosofia de controle-fluxo), significa que algo entrou em um estado instável (embora talvez recuperável). Estados instáveis são ruins e devem nos manter acordados à noite se realmente tivermos exceções evitáveis (na verdade, me assusta, sem mentiras).try...catch
o fluxo de controle vai contra o que são comumente consideradas melhores práticas. Isso significa que leva mais tempo para alguém novato em um projeto aprender o projeto, o que significa um aumento no número de horas-homem para absolutamente nenhum ganho.try{ obj.openSomething(); /*something which causes exception*/ obj.doStuff(); obj.closeSomething();}catch(Exception e){obj.closeSomething();}
. Em umif...else
cenário mais tradicional,closeSomething()
é menos provável (novamente, experiência pessoal) ser um trabalho de copiar e colar. (É certo que esse argumento em particular tem mais a ver com pessoas que conheci do que com a própria filosofia).fonte
Meu argumento principal é que o uso de try / catch para a lógica interrompe o fluxo lógico. Seguir a "lógica" através de construções não-lógicas é (para mim) contra-intuitivo e confuso. Estou acostumado a ler minha lógica como "se Condição então A mais B". Ler a mesma instrução que "Tente uma captura e execute B" é estranho. Seria ainda mais estranho se a declaração
A
fosse uma atribuição simples, exigindo código extra para forçar uma exceção secondition
for falsa (e fazer isso provavelmente exigiria umaif
declaração de qualquer maneira).fonte
Bem, apenas uma questão de etiqueta, antes de "iniciar uma discussão com eles", como você afirma, gostaria de perguntar "Por que eles usam tratamento de exceção em todos esses lugares diferentes?"
Quero dizer, existem várias possibilidades:
... Eu acho que tudo isso é igualmente provável. Então, basta perguntar-lhes bem.
fonte
O Try Catch só deve ser usado para manipulação de exceções. Mais ainda, manipulação de exceção específica. Sua tentativa de captura deve capturar apenas as exceções esperadas; caso contrário, ela não está bem formada. Se você precisar usar um catch all try catch, provavelmente está fazendo algo errado.
EDITAR:
Razão: Você pode argumentar que, se você usar o try catch como um operador condicional, como você considerará as exceções REAL?
fonte
catch
cláusula diferente daquela que captura exceções "não reais"?Exceções são para quando coisas excepcionais acontecem. O programa está funcionando de acordo com o fluxo de trabalho regular excepcional?
fonte
Razão para usar exceções:
Razões para não usar exceções:
No final:
O objetivo é escrever um código que comunique o que está acontecendo. Exceções podem ajudar / dificultar isso, dependendo do que o código está fazendo. Uma tentativa / captura em python para um KeyError em uma referência de dicionário é perfeitamente (desde que você conheça o idioma) a tentativa / captura para o mesmo KeyError a cinco camadas de função é perigosa.
fonte
Eu uso try-> catch como controle de fluxo em determinadas situações. Se sua lógica principal depende de algo que falha, mas você não deseja apenas lançar uma exceção e sair ... É para isso que serve um bloco try-> catch. Eu escrevo muitos scripts unix do lado do servidor não monitorados e é muito mais importante para eles não falharem do que para eles falharem lindamente.
Portanto, tente o Plano A, e se o Plano A morrer, pegue e execute com o Plano B ... E se o plano B falhar, use finalmente para iniciar o Plano C, que corrigirá um dos serviços com falha em A ou B ou na página mim.
fonte
Depende do idioma e talvez da implementação que está sendo usada. O padrão em C ++ é que as exceções devem ser salvas para eventos verdadeiramente excepcionais. Por outro lado, em Python, uma das diretrizes é " é mais fácil pedir perdão do que permissão "; portanto, a integração de blocos try / except na lógica do programa é incentivada.
fonte
É um mau hábito, mas faço-o de vez em quando em python. Como, em vez de verificar se existe um arquivo, apenas tento excluí-lo. Se isso der uma exceção, eu pego e continuo sabendo que não estava lá. <== (não necessariamente verdadeiro se outro usuário possuir o arquivo, mas eu o faço no diretório inicial do usuário para que isso NÃO DEVE acontecer).
Ainda assim, usar em excesso é uma prática ruim.
fonte
Gosto da maneira como a Apple o define : as exceções são apenas para erros de programadores e erros fatais de tempo de execução. Caso contrário, use códigos de erro. Isso me ajuda a decidir quando usá-lo, pensando comigo mesmo: "Isso foi um erro de programador?"
fonte
Isso soa como usar o tratamento de exceções para criar lógica goto sobre um idioma que não possui um comando goto.
Por muitas razões pelas quais a lógica goto é ruim, você pode consultar esta pergunta: https://stackoverflow.com/questions/46586/goto-still-considered-harmful
fonte