web-dev-qa-db-fra.com

Arguments pour ou contre l'utilisation de Try / Catch comme opérateurs logiques

Je viens de découvrir un joli code dans notre application d'entreprise qui utilise des blocs Try-Catch comme opérateurs logiques.
Signification, "faites du code, si cela génère cette erreur, faites ce code, mais si cela génère cette erreur, faites cette troisième chose à la place".
Il utilise "Finalement" comme instruction "else" qui apparaît.
Je sais que c'est intrinsèquement faux, mais avant de choisir un combat, j'espérais des arguments bien pensés.
Et hé, si vous avez des arguments POUR l'utilisation de Try-Catch de cette manière, veuillez le dire.

Pour tous ceux qui se demandent, le langage est C # et le code en question est d'environ 30+ lignes et recherche des exceptions spécifiques, il ne gère pas TOUTES les exceptions.

38
James P. Wright

La gestion des exceptions a tendance à être un moyen coûteux de gérer le contrôle de flux (certainement pour C # et Java).

Le runtime fait beaucoup de travail quand un objet d'exception est construit - obtenir la trace de la pile ensemble, déterminer où l'exception est gérée et plus encore.

Tout cela coûte en mémoire et en ressources CPU qui n'ont pas besoin d'être étendues si des instructions de contrôle de flux sont utilisées pour le contrôle de flux.

De plus, il y a un problème sémantique. Les exceptions concernent les situations exceptionnelles et non le contrôle normal du débit. Il faut utiliser la gestion des exceptions pour gérer des situations imprévues/exceptionnelles, pas comme un flux de programme normal, car sinon, une exception non interceptée vous en dira beaucoup moins.

En dehors de ces deux, il y a la question des autres qui lisent le code. La plupart des programmeurs ne s'attendent pas à utiliser des exceptions de cette manière, donc la lisibilité et la compréhension de votre code en souffrent. Quand on voit "Exception", on pense - quelque chose de mauvais s'est produit, quelque chose qui n'est pas censé se produire normalement. Donc, utiliser des exceptions de cette manière est tout simplement déroutant.

54
Oded

Je viens de découvrir un joli code dans notre application d'entreprise qui utilise des blocs Try-Catch comme opérateurs logiques. Ce qui signifie, "faites du code, si cela génère cette erreur, faites ce code, mais si cela génère cette erreur, faites cette troisième chose à la place". Il utilise "Enfin" comme instruction "else", il apparaît. Je sais que cela est mal intrinsèquement ...

Comment sais-tu ça? J'ai abandonné toutes ces sortes de "connaissances" et je crois maintenant que le code le plus simple est le meilleur. Supposons que vous souhaitiez convertir une chaîne en Facultatif qui est vide si l'analyse échoue. Il n'y a rien de mal à:

try { 
    return Optional.of(Long.valueOf(s)); 
} catch (NumberFormatException) { 
    return Optional.empty(); 
}

Je suis totalement en désaccord avec l'interprétation habituelle de "Les exceptions concernent des conditions exceptionnelles". Lorsqu'une fonction ne peut pas retourner une valeur utilisable ou qu'une méthode ne peut pas remplir ses post-conditions, lève une exception. Peu importe la fréquence de ces exceptions jusqu'à ce qu'il y ait un problème de performances démontré.

Les exceptions simplifient le code, en permettant la séparation de la gestion des erreurs du flux normal. Écrivez simplement le code le plus simple possible, et s'il est plus facile d'utiliser try-catch ou de lever une exception, faites-le.

Les exceptions simplifient les tests en réduisant le nombre de chemins dans le code. Une fonction sans branche terminera ou lèvera une exception. Une fonction avec plusieurs instructions if pour vérifier les codes d'erreur a plusieurs chemins possibles. Il est très facile de se tromper dans l'une des conditions ou de l'oublier complètement, de sorte qu'une condition d'erreur est ignorée.

16
kevin cline

Les travaux de débogage et de maintenance sont très difficiles lorsque le flux de contrôle est effectué à l'aide d'exceptions.

De manière inhérente, les exceptions sont conçues pour être un mécanisme permettant de modifier le flux de contrôle normal de votre programme - d'effectuer des activités inhabituelles, provoquant des effets secondaires inhabituels, comme un moyen de sortir d'une liaison particulièrement étroite qui ne peut pas être gérée avec moins de complexité. veux dire. Les exceptions sont exceptionnelles. Cela signifie que, selon l'environnement dans lequel vous travaillez, l'utilisation d'exceptions pour le flux de contrôle régulier peut entraîner:

  • Inefficiency Les cerceaux supplémentaires que l'environnement doit parcourir pour effectuer en toute sécurité les changements de contexte relativement difficiles requis pour les exceptions nécessitent une instrumentation et des ressources.

  • Difficultés de débogage Parfois, des informations utiles (lorsque vous essayez de déboguer un programme) sont rejetées par la fenêtre lorsque des exceptions se produisent. Vous pouvez perdre la trace de l'état ou de l'historique du programme qui sont pertinents pour comprendre le comportement au moment de l'exécution

  • Problèmes de maintenance Le flux d'exécution est difficile à suivre à travers les sauts d'exception. Au-delà de cela, une exception peut être levée à partir du code de type boîte noire, qui peut ne pas se comporter de manière facile à comprendre lors du lancement d'une exception.

  • Mauvaises décisions de conception Les programmes construits de cette manière encouragent un état d'esprit qui, dans la plupart des cas, ne permet pas facilement de résoudre les problèmes avec élégance. La complexité du programme final décourage le programmeur de comprendre pleinement son exécution et encourage la prise de décisions qui conduisent à des améliorations à court terme avec des coûts à long terme élevés.

13
blueberryfields

J'ai vu ce modèle utilisé plusieurs fois.

Il y a 2 problèmes majeurs:

  • C'est extrêmement coûteux (instanciation de l'objet d'exception, collecte de la pile d'appels, etc.). Certains compilateurs pourraient en fait être en mesure de l'optimiser, mais je ne compterais pas là-dessus dans ce cas, car les exceptions ne sont pas destinées à une telle utilisation, vous ne pouvez donc pas vous attendre à ce que les gens l'optimisent.
  • L'utilisation d'exceptions pour le flux de contrôle est en fait un saut, un peu comme un goto. Les sauts sont considérés comme dangereux, pour plusieurs raisons. Ils doivent être utilisés si toutes les alternatives présentent des inconvénients considérables. En fait, dans tout mon code, je ne me souviens que de 2 cas, où les sauts étaient clairement la meilleure solution.
10
back2dos

Parfois, les exceptions sont les plus rapides. J'ai vu des cas où les exceptions d'objets nuls étaient plus rapides même en Java que l'utilisation de structures de contrôle (je ne peux pas citer l'étude pour le moment, vous devrez donc me faire confiance). Le problème arrive quand Java doit réellement prendre le temps et remplir la trace de pile d'une classe d'exception personnalisée au lieu d'utiliser des natives (qui semblent être au moins partiellement mises en cache). Avant de dire que quelque chose est unilatéralement plus rapide ou plus lent, il serait bon de comparer.

Dans Python c'est non seulement plus rapide, mais il est beaucoup plus correct de faire quelque chose qui pourrait provoquer une exception, puis gérer l'erreur. Oui, vous pouvez appliquer un système de type, mais cela va à l'encontre de la philosophie du langage - au lieu de cela, vous devriez simplement essayer d'appeler la méthode et de capturer le résultat! (Tester si un fichier est accessible en écriture est similaire - essayez simplement d'écrire dessus et de détecter l'erreur).

J'ai vu des fois où il est plus rapide de faire quelque chose de stupide comme des tables de requête qui n'étaient pas là que de déterminer si une table existe en PHP + MySQL (la question où je compare c'est en fait ma réponse acceptée avec votes négatifs).

Cela dit, l'utilisation des exceptions devrait être limitée pour plusieurs raisons:

  1. Ingestion accidentelle d'exceptions imbriquées. Ceci est majeur. Si vous attrapez une exception profondément imbriquée que quelqu'un d'autre essaie de gérer, vous venez de tirer votre collègue programmeur (peut-être même vous!) Dans le pied.
  2. Les tests deviennent non évidents. Un bloc de code qui contient une exception pourrait avoir plusieurs erreurs. Un booléen, d'autre part, alors que théoriquement il peut être ennuyeux de déboguer, ce n'est généralement pas le cas. Cela est d'autant plus vrai que les adhérents du flux de contrôle try...catch En général (d'après mon expérience) ne suivent pas la philosophie "minimiser le code dans le bloc try".
  3. Il ne permet pas un bloc else if. Assez dit (et si quelqu'un contre avec un "Mais ils pourraient utiliser différentes classes d'exceptions", ma réponse est "Allez dans votre chambre" et ne sortez pas avant d'avoir réfléchi à ce que vous avez dit. ")
  4. Il est grammaticalement trompeur.Exception, pour le reste du monde (comme dans les non adhérents à la philosophie de contrôle des flux try...catch), Signifie que quelque chose a est entré dans un état instable (mais peut-être récupérable). Les états instables sont [~ # ~] mauvais [~ # ~] et cela devrait nous tenir tous éveillés la nuit si nous avons réellement des exceptions évitables (cela me fait flipper, pas de mensonge).
  5. Il n'est pas conforme au style de codage commun. Notre travail, à la fois avec notre code et avec nos interfaces utilisateur, est de rendre le monde le plus évident possible. Le flux de contrôle try...catch Va à l'encontre des meilleures pratiques communément considérées. Cela signifie qu'il faut plus de temps à une personne novice pour apprendre le projet, ce qui signifie une augmentation du nombre d'heures-homme sans aucun gain.
  6. Cela conduit souvent à la duplication de code. Enfin les blocs, bien que cela ne soit pas strictement nécessaire, doivent résoudre tous les pointeurs pendants qui ont été laissés ouverts par le bloc try interrompu. Essayez donc les blocs. Cela signifie que vous pouvez avoir une try{ obj.openSomething(); /*something which causes exception*/ obj.doStuff(); obj.closeSomething();}catch(Exception e){obj.closeSomething();}. Dans un scénario plus traditionnel, if...else, La closeSomething() est moins susceptible (encore une fois, une expérience personnelle) d'être un travail de copier-coller. (Certes, cet argument particulier a plus à voir avec les gens que j'ai rencontrés qu'avec la philosophie elle-même).
9
cwallenpoole

Mon argument principal est que l'utilisation de try/catch pour la logique rompt le flux logique. Suivre la "logique" à travers des constructions non logiques est (pour moi) contre-intuitif et déroutant. J'ai l'habitude de lire ma logique comme "si Condition alors A sinon B". La lecture de la même déclaration que "Essayez une capture puis exécutez B" semble bizarre. Ce serait encore plus étrange si l'instruction A est une affectation simple, nécessitant du code supplémentaire pour forcer une exception si condition est faux (et cela nécessiterait probablement une instruction if- en tous cas).

Eh bien, juste une question d'étiquette, avant de "commencer une discussion avec eux", comme vous le dites, je voudrais bien demander "Pourquoi ils utilisent la gestion des exceptions dans tous ces différents endroits?"

Je veux dire, il y a plusieurs possibilités:

  1. ils sont incompétents
  2. il y a une raison parfaitement valable pour ce qu'ils ont fait, qui peut ne pas apparaître à première vue.
  3. parfois, c'est une question de goût et peut simplifier un flux de programme complexe.
  4. C'est une relique du passé que personne n'a encore changé et ils seraient heureux si quelqu'un le faisait

... Je pense que tout cela est tout aussi probable. Alors, demandez-leur gentiment.

2
dagnelies

Raison d'utiliser des exceptions:

  1. Si vous oubliez d'attraper une circonstance exceptionnelle, votre programme mourra et la trace de la pile vous dira exactement pourquoi. Si vous oubliez de gérer la valeur de retour pour une circonstance d'exception, il n'est pas indiqué à quelle distance votre programme affichera un comportement incorrect.
  2. L'utilisation de valeurs de retour ne fonctionne que s'il existe une valeur sentinelle que vous pouvez renvoyer. Si toutes les valeurs de retour possibles sont déjà valides, qu'allez-vous faire?
  3. Une exception portera des informations supplémentaires sur ce qui s'est passé qui peuvent être utiles.

Raisons de ne pas utiliser d'exceptions:

  1. De nombreuses langues ne sont pas conçues pour rendre les exceptions rapides.
  2. Les exceptions qui parcourent plusieurs couches dans la pile peuvent laisser un état incohérent dans leur sillage

À la fin:

Le but est d'écrire du code qui communique ce qui se passe. Les exceptions peuvent aider/gêner cela en fonction de ce que fait le code. Un try/catch dans python pour une KeyError sur une référence de dictionnaire est parfaitement (tant que vous connaissez la langue) try/catch pour la même couche de KeyError à cinq couches de fonction est dangereux.

1
Winston Ewert

Try Catch ne doit être utilisé que pour la gestion des exceptions. Plus encore, la gestion des exceptions spécifiques. Votre tentative de capture ne devrait intercepter que les exceptions attendues, sinon elle n'est pas bien formée. Si vous avez besoin d'utiliser un catch, essayez de catch, alors vous faites probablement quelque chose de mal.

ÉDITER:

Raison: vous pouvez faire valoir que si vous utilisez try catch comme opérateur conditionnel, comment allez-vous tenir compte des VRAIES exceptions?

1
AJC

Les exceptions sont quand une chose exceptionnelle se produit. Le programme fonctionne-t-il selon un flux de travail régulier est-il exceptionnel?

1
jfrobishow

Cela dépend de la langue et peut-être de l'implémentation utilisée. La norme en C++ est que les exceptions doivent être enregistrées pour des événements vraiment exceptionnels. D'autre part, dans Python l'une des directives est " il est plus facile de demander pardon que pour permission ", donc l'intégration des blocs try/except dans la logique du programme est encouragée.

0
Charles E. Grant

J'utilise try-> catch comme contrôle de flux dans certaines situations. Si votre logique principale dépend de quelque chose qui échoue, mais vous ne voulez pas simplement lever une exception et quitter ... C'est à cela que sert un bloc try-> catch. J'écris beaucoup de scripts Unix côté serveur non surveillés, et il est beaucoup plus important pour eux de ne pas échouer que pour eux d'échouer joliment.

Alors essayez Plan A, et si le Plan A meurt, attrapez et exécutez avec le Plan B ... Et si le plan B échoue , utilisez enfin pour lancer le plan C, qui corrigera l'un des services défaillants dans A ou B, ou me mettra en page.

0
Satanicpuppy