Pour éviter toutes les réponses standard sur lesquelles j'aurais pu googler, je vais vous donner un exemple que vous pouvez tous attaquer à volonté.
C # et Java (et trop d'autres) ont avec beaucoup de types des comportements de «débordement» que je n'aime pas du tout (par type.MaxValue + type.SmallestValue == type.MinValue
exemple:) int.MaxValue + 1 == int.MinValue
.
Mais, vu ma nature vicieuse, j'ajouterai une insulte à cette blessure en étendant ce comportement à, disons, un DateTime
type Overridden . (Je sais que DateTime
c'est scellé dans .NET, mais pour le bien de cet exemple, j'utilise un pseudo langage qui est exactement comme C #, sauf pour le fait que DateTime n'est pas scellé).
La Add
méthode remplacée :
/// <summary>
/// Increments this date with a timespan, but loops when
/// the maximum value for datetime is exceeded.
/// </summary>
/// <param name="ts">The timespan to (try to) add</param>
/// <returns>The Date, incremented with the given timespan.
/// If DateTime.MaxValue is exceeded, the sum wil 'overflow' and
/// continue from DateTime.MinValue.
/// </returns>
public DateTime override Add(TimeSpan ts)
{
try
{
return base.Add(ts);
}
catch (ArgumentOutOfRangeException nb)
{
// calculate how much the MaxValue is exceeded
// regular program flow
TimeSpan saldo = ts - (base.MaxValue - this);
return DateTime.MinValue.Add(saldo)
}
catch(Exception anyOther)
{
// 'real' exception handling.
}
}
Bien sûr, un if pourrait résoudre cela tout aussi facilement, mais le fait reste que je ne vois pas pourquoi vous ne pouvez pas utiliser d'exceptions (logiquement, c'est-à-dire que lorsque les performances sont un problème, dans certains cas, les exceptions doivent être évitées ).
Je pense que dans de nombreux cas, ils sont plus clairs que les structures if et ne rompent aucun contrat de la méthode.
À mon humble avis, la réaction «Ne jamais les utiliser pour le déroulement régulier du programme» que tout le monde semble avoir n'est pas aussi bien insuffisante que la force de cette réaction peut le justifier.
Ou est-ce que je me trompe?
J'ai lu d'autres articles traitant de toutes sortes de cas particuliers, mais mon point est qu'il n'y a rien de mal à cela si vous êtes tous les deux:
- Clair
- Respectez le contrat de votre méthode
Tirez sur moi.
if
déclaration beaucoup plus courte et auto-documentée . Vous trouverez cela très difficile. En d'autres termes: votre prémisse même est imparfaite, et les conclusions que vous en tirez sont donc fausses.Réponses:
Avez-vous déjà essayé de déboguer un programme générant cinq exceptions par seconde dans le cours normal de son fonctionnement?
J'ai.
Le programme était assez complexe (c'était un serveur de calcul distribué), et une légère modification d'un côté du programme pouvait facilement casser quelque chose dans un endroit totalement différent.
J'aurais aimé pouvoir lancer le programme et attendre que des exceptions se produisent, mais il y a eu environ 200 exceptions pendant le démarrage dans le cours normal des opérations
Mon point: si vous utilisez des exceptions pour des situations normales, comment repérez-vous les situations inhabituelles (c'est-à-dire exceptionnelles )?
Bien sûr, il existe d'autres bonnes raisons de ne pas trop utiliser les exceptions, en particulier en termes de performances
la source
Les exceptions sont essentiellement des
goto
déclarations non locales avec toutes les conséquences de ces dernières. L'utilisation d'exceptions pour le contrôle de flux viole un principe de moindre étonnement , rendre les programmes difficiles à lire (rappelez-vous que les programmes sont d'abord écrits pour les programmeurs).De plus, ce n'est pas ce à quoi s'attendent les éditeurs de compilateurs. Ils s'attendent à ce que des exceptions soient lancées rarement, et ils laissent généralement le
throw
code être assez inefficace. Lancer des exceptions est l'une des opérations les plus coûteuses dans .NET.Cependant, certains langages (notamment Python) utilisent des exceptions comme constructions de contrôle de flux. Par exemple, les itérateurs lèvent une
StopIteration
exception s'il n'y a plus d'éléments. Même les constructions de langage standard (telles quefor
) reposent sur cela.la source
goto
, bien sûr, les exceptions interagissent correctement avec votre pile d'appels et avec la portée lexicale, et ne laissez pas la pile ou les portées dans le désordre.Ma règle de base est:
Le problème que je vois avec des exceptions est d'un point de vue purement syntaxique (je suis presque sûr que la surcharge de performance est minime). Je n'aime pas les blocs d'essai partout.
Prenons cet exemple:
.. Un autre exemple pourrait être lorsque vous avez besoin d'assigner quelque chose à un handle à l'aide d'une fabrique, et cette fabrique pourrait lever une exception:
Soo, personnellement, je pense que vous devriez garder des exceptions pour les conditions d'erreur rares (mémoire insuffisante, etc.) et utiliser des valeurs de retour (classes de valeur, structs ou enums) pour faire votre vérification d'erreurs à la place.
J'espère que j'ai bien compris votre question :)
la source
Une première réaction à beaucoup de réponses:
Bien sûr! Mais un si n'est pas plus clair tout le temps.
Cela ne devrait pas être étonnant, par exemple: divide (1 / x) catch (divisionByZero) est plus clair que tout pour moi (chez Conrad et d'autres). Le fait que ce type de programmation ne soit pas attendu est purement conventionnel et, en fait, toujours pertinent. Peut-être que dans mon exemple, un si serait plus clair.
Mais DivisionByZero et FileNotFound sont d'ailleurs plus clairs que si.
Bien sûr, s'il est moins performant et qu'il a besoin d'un million de fois par seconde, vous devriez bien sûr l'éviter, mais je n'ai toujours pas lu de bonne raison d'éviter la conception globale.
En ce qui concerne le principe du moindre étonnement: il y a ici un danger de raisonnement circulaire: supposons qu'une communauté entière utilise un mauvais design, ce design deviendra attendu! Par conséquent, le principe ne peut pas être un graal et doit être considéré avec soin.
Dans de nombreuses réactions qc. comme ça brille à travers. Attrapez-les, non? Votre méthode doit être claire, bien documentée et respectueuse de son contrat. Je ne comprends pas cette question, je dois l'admettre.
Débogage sur toutes les exceptions: pareil, cela se fait parfois car la conception de ne pas utiliser d'exceptions est courante. Ma question était: pourquoi est-ce courant en premier lieu?
la source
x
avant d'appeler1/x
? 2) Enveloppez-vous chaque opération de division dans un bloc try-catch pour attraperDivideByZeroException
? 3) Quelle logique mettez-vous dans le bloc catch pour récupérerDivideByZeroException
?openConfigFile();
peut être suivi d'un FileNotFound intercepté avec{ createDefaultConfigFile(); setFirstAppRun(); }
une exception FileNotFound gérée correctement; pas de plantage, améliorons l'expérience de l'utilisateur final, pas pire. Vous pouvez dire "Mais que se passerait-il si ce n'était pas vraiment la première manche et qu'ils l'obtiennent à chaque fois?" Au moins, l'application s'exécute à chaque fois et ne plante pas à chaque démarrage! Sur un 1 à 10 "c'est terrible": chaque démarrage = 3 ou 4 "first-run", planter chaque démarrage = 10.x
avant d'appeler1/x
, car c'est généralement bien. Le cas exceptionnel est le cas où ce n'est pas bien. Nous ne parlons pas de bouleversement de la terre ici, mais par exemple pour un entier de base donné au hasardx
, seulement 1 sur 4294967296 échouerait à faire la division. C'est exceptionnel et les exceptions sont un bon moyen de gérer cela. Cependant, vous pouvez utiliser des exceptions pour implémenter l'équivalent d'uneswitch
instruction, mais ce serait assez idiot.Avant les exceptions, en C, il y avait
setjmp
etlongjmp
cela pouvait être utilisé pour accomplir un déroulement similaire du cadre de pile.Ensuite, la même construction a reçu un nom: "Exception". Et la plupart des réponses s'appuient sur la signification de ce nom pour argumenter sur son utilisation, affirmant que les exceptions sont destinées à être utilisées dans des conditions exceptionnelles. Ce n'était jamais l'intention de l'original
longjmp
. Il y avait juste des situations où vous deviez interrompre le flux de contrôle sur de nombreux cadres de pile.Les exceptions sont légèrement plus générales dans la mesure où vous pouvez également les utiliser dans le même cadre de pile. Cela soulève des analogies avec ce
goto
que je pense être faux. Les Gotos sont une paire étroitement couplée (tout commesetjmp
etlongjmp
). Les exceptions font suite à une publication / abonnement faiblement couplée qui est beaucoup plus propre! Par conséquent, les utiliser dans le même cadre de pile n'est pas la même chose que d'utilisergoto
s.La troisième source de confusion est de savoir s'il s'agit d'exceptions cochées ou non cochées. Bien sûr, les exceptions non contrôlées semblent particulièrement horribles à utiliser pour contrôler le flux et peut-être pour beaucoup d'autres choses.
Les exceptions cochées sont cependant idéales pour contrôler le flux, une fois que vous avez surmonté tous les problèmes victoriens et que vous vivez un peu.
Mon utilisation préférée est une séquence d'
throw new Success()
un long fragment de code qui essaie une chose après l'autre jusqu'à ce qu'elle trouve ce qu'elle recherche. Chaque chose - chaque élément de logique - peut avoir une imbrication arbitraire, il enbreak
est donc de même pour tout type de test de condition. Leif-else
motif est cassant. Si je modifie uneelse
syntaxe ou que je gâche la syntaxe d'une autre manière, il y a un bug poilu.L'utilisation de
throw new Success()
linéarise le flux de code. J'utilise desSuccess
classes définies localement - vérifiées bien sûr - pour que si j'oublie de l'attraper, le code ne se compile pas. Et je n'attrape pas lesSuccess
es d' une autre méthode .Parfois, mon code vérifie une chose après l'autre et ne réussit que si tout va bien. Dans ce cas, j'ai une linéarisation similaire en utilisant
throw new Failure()
.L'utilisation d'une fonction distincte perturbe le niveau naturel de compartimentation. La
return
solution n'est donc pas optimale. Je préfère avoir une ou deux pages de code au même endroit pour des raisons cognitives. Je ne crois pas au code ultra-finement divisé.Ce que font les JVM ou les compilateurs est moins pertinent pour moi à moins qu'il n'y ait un hotspot. Je ne peux pas croire qu'il y ait une raison fondamentale pour les compilateurs de ne pas détecter les exceptions lancées et capturées localement et de les traiter simplement comme des
goto
s très efficaces au niveau du code machine.En ce qui concerne leur utilisation dans les fonctions de contrôle du flux - c'est-à-dire pour les cas courants plutôt que pour les cas exceptionnels - je ne vois pas en quoi ils seraient moins efficaces que de multiples pannes, tests de condition, retours à parcourir trois cadres de pile au lieu de simplement restaurer le pointeur de pile.
Personnellement, je n'utilise pas le motif sur les cadres de pile et je peux voir comment il faudrait une conception sophistiquée pour le faire avec élégance. Mais utilisé avec parcimonie, ça devrait aller.
Enfin, en ce qui concerne les programmeurs vierges surprenants, ce n'est pas une raison impérieuse. Si vous les initiez doucement à la pratique, ils apprendront à l'aimer. Je me souviens que C ++ avait l'habitude de surprendre et d'effrayer les programmeurs C.
la source
return
alternative -pattern nécessiterait deux fonctions pour chacune de ces fonctions. Un externe pour préparer la réponse de servlet ou d'autres actions similaires, et un interne pour effectuer le calcul. PS: Un professeur d'anglais suggérerait probablement que j'utilise "étonnant" plutôt que "surprenant" dans le dernier paragraphe :-)La réponse standard est que les exceptions ne sont pas régulières et doivent être utilisées dans des cas exceptionnels.
Une raison, qui est importante pour moi, est que lorsque je lis une
try-catch
structure de contrôle dans un logiciel que je maintiens ou débogue, j'essaie de savoir pourquoi le codeur d'origine a utilisé une gestion des exceptions au lieu d'uneif-else
structure. Et j'espère trouver une bonne réponse.N'oubliez pas que vous écrivez du code non seulement pour l'ordinateur, mais également pour d'autres codeurs. Il existe une sémantique associée à un gestionnaire d'exceptions que vous ne pouvez pas jeter simplement parce que la machine ne s'en soucie pas.
la source
Et les performances? Lors du test de charge d'une application Web .NET, nous avons dépassé 100 utilisateurs simulés par serveur Web jusqu'à ce que nous corrigions une exception courante et que ce nombre soit passé à 500 utilisateurs.
la source
Josh Bloch traite ce sujet en détail dans Effective Java. Ses suggestions sont éclairantes et devraient également s'appliquer à .Net (sauf pour les détails).
En particulier, des exceptions devraient être utilisées pour des circonstances exceptionnelles. Les raisons en sont principalement liées à la convivialité. Pour qu'une méthode donnée soit utilisable au maximum, ses conditions d'entrée et de sortie doivent être contraintes au maximum.
Par exemple, la deuxième méthode est plus facile à utiliser que la première:
Dans les deux cas, vous devez vous assurer que l'appelant utilise correctement votre API. Mais dans le second cas, vous en avez besoin (implicitement). Les exceptions logicielles seront toujours levées si l'utilisateur n'a pas lu le javadoc, mais:
Le point fondamental est que les exceptions ne doivent pas être utilisées comme codes de retour, en grande partie parce que vous avez compliqué non seulement VOTRE API, mais aussi l'API de l'appelant.
Faire ce qu'il faut a un coût, bien sûr. Le coût est que chacun doit comprendre qu'il doit lire et suivre la documentation. Espérons que ce soit le cas de toute façon.
la source
Je pense que vous pouvez utiliser des exceptions pour le contrôle de flux. Il y a cependant un revers à cette technique. La création d'exceptions est une chose coûteuse, car elles doivent créer une trace de pile. Donc, si vous souhaitez utiliser les exceptions plus souvent que pour simplement signaler une situation exceptionnelle, vous devez vous assurer que la création des traces de pile n'influence pas négativement vos performances.
La meilleure façon de réduire le coût de création d'exceptions est de remplacer la méthode fillInStackTrace () comme ceci:
Une telle exception n'aura aucune trace de pile remplie.
la source
Je ne vois pas vraiment comment vous contrôlez le déroulement du programme dans le code que vous avez cité. Vous ne verrez jamais une autre exception en plus de l'exception ArgumentOutOfRange. (Ainsi, votre deuxième clause catch ne sera jamais atteinte). Tout ce que vous faites est d'utiliser un lancer extrêmement coûteux pour imiter une instruction if.
De plus, vous n'effectuez pas les opérations les plus sinistres où vous lancez simplement une exception uniquement pour qu'elle soit interceptée ailleurs pour effectuer le contrôle de flux. Vous gérez en fait un cas exceptionnel.
la source
Voici les meilleures pratiques que j'ai décrites dans mon article de blog :
la source
Parce que le code est difficile à lire, vous pouvez avoir des difficultés à le déboguer, vous introduirez de nouveaux bogues lors de la correction des bogues après une longue période, c'est plus cher en termes de ressources et de temps, et cela vous ennuie si vous déboguez votre code et le débogueur s'arrête à l'apparition de chaque exception;)
la source
Outre les raisons indiquées, une des raisons de ne pas utiliser d'exceptions pour le contrôle de flux est que cela peut considérablement compliquer le processus de débogage.
Par exemple, lorsque j'essaie de localiser un bogue dans VS, j'active généralement "interrompre toutes les exceptions". Si vous utilisez des exceptions pour le contrôle de flux, je vais régulièrement interrompre le débogueur et je devrai continuer à ignorer ces exceptions non exceptionnelles jusqu'à ce que j'arrive au vrai problème. Cela risque de rendre quelqu'un fou !!
la source
Supposons que vous ayez une méthode qui effectue certains calculs. Il y a de nombreux paramètres d'entrée qu'il doit valider, puis renvoyer un nombre supérieur à 0.
En utilisant des valeurs de retour pour signaler une erreur de validation, c'est simple: si la méthode retournait un nombre inférieur à 0, une erreur se produisait. Comment savoir alors quel paramètre n'a pas validé?
Je me souviens de mes jours C, beaucoup de fonctions renvoyaient des codes d'erreur comme celui-ci:
etc.
Est-ce vraiment moins lisible que de lancer et d'attraper une exception?
la source
Vous pouvez utiliser la griffe d'un marteau pour tourner une vis, tout comme vous pouvez utiliser des exceptions pour contrôler le flux. Cela ne signifie pas que c'est l' utilisation prévue de la fonctionnalité. Les
if
états de conditions, dont l' utilisation Exprime destinée est contrôler le débit.Si vous utilisez une fonctionnalité de manière non intentionnelle tout en choisissant de ne pas utiliser la fonctionnalité conçue à cet effet, il y aura un coût associé. Dans ce cas, la clarté et les performances ne souffrent d'aucune réelle valeur ajoutée. Qu'est-ce que l'utilisation d'exceptions vous achète par rapport à la
if
déclaration largement acceptée ?Dit autrement: ce n'est pas parce que vous pouvez que vous devriez le faire .
la source
if
une utilisation normale ou l'utilisation d'exceptions n'est pas intentionnelle parce qu'elle n'est pas intentionnelle (argument circulaire)?public class ExitHelper{ public static void cleanExit() { cleanup(); System.exit(1); } }
Alors appelez simplement cela au lieu de lancer:ExitHelper.cleanExit();
Si votre argument était valable, alors ce serait l'approche préférée et il n'y aurait pas d'exceptions. Vous dites essentiellement "La seule raison des exceptions est de planter d'une manière différente".if
.Comme d'autres l'ont mentionné à de nombreuses reprises, le principe du moindre étonnement vous interdira d'utiliser des exceptions de manière excessive à des fins de contrôle du flux uniquement. D'un autre côté, aucune règle n'est correcte à 100%, et il y a toujours des cas où une exception est "juste le bon outil" - un peu comme
goto
elle-même, d'ailleurs, qui se présente sous la formebreak
etcontinue
dans des langages comme Java, qui sont souvent le moyen idéal pour sortir de boucles fortement imbriquées, qui ne sont pas toujours évitables.Le billet de blog suivant explique un cas d'utilisation plutôt complexe mais aussi plutôt intéressant pour un non-local
ControlFlowException
:Il explique comment à l'intérieur de jOOQ (une bibliothèque d'abstraction SQL pour Java) , de telles exceptions sont parfois utilisées pour abandonner le processus de rendu SQL prématurément lorsqu'une condition "rare" est remplie.
Des exemples de telles conditions sont:
Trop de valeurs de liaison sont rencontrées. Certaines bases de données ne prennent pas en charge des nombres arbitraires de valeurs de liaison dans leurs instructions SQL (SQLite: 999, Ingres 10.1.0: 1024, Sybase ASE 15.5: 2000, SQL Server 2008: 2100). Dans ces cas, jOOQ interrompt la phase de rendu SQL et restitue l'instruction SQL avec des valeurs de liaison intégrées. Exemple:
Si nous extrayions explicitement les valeurs de liaison de la requête AST pour les compter à chaque fois, nous gaspillerions de précieux cycles de processeur pour ces 99,9% des requêtes qui ne souffrent pas de ce problème.
Une certaine logique n'est disponible qu'indirectement via une API que nous ne voulons exécuter que «partiellement». La
UpdatableRecord.store()
méthode génère une instructionINSERT
orUPDATE
, en fonctionRecord
des indicateurs internes du. De «l'extérieur», nous ne savons pas dans quel type de logique se trouvestore()
(par exemple le verrouillage optimiste, la gestion de l'écouteur d'événements, etc.), nous ne voulons donc pas répéter cette logique lorsque nous stockons plusieurs enregistrements dans une instruction batch, où nous aimerions avoirstore()
uniquement généré l'instruction SQL, pas réellement l'exécuter. Exemple:Si nous avions externalisé la
store()
logique en une API "réutilisable" qui peut être personnalisée pour éventuellement ne pas exécuter le SQL, nous chercherions à créer une API plutôt difficile à maintenir, difficilement réutilisable.Conclusion
En substance, notre utilisation de ces non-locaux
goto
est juste dans le sens de ce que [Mason Wheeler] [5] a dit dans sa réponse:Les deux utilisations de
ControlFlowExceptions
étaient plutôt faciles à mettre en œuvre par rapport à leurs alternatives, ce qui nous permet de réutiliser un large éventail de logique sans la refactoriser hors des composants internes pertinents.Mais le sentiment que cela est un peu une surprise pour les futurs responsables demeure. Le code semble plutôt délicat et bien que ce soit le bon choix dans ce cas, nous préférerions toujours ne pas utiliser d'exceptions pour le flux de contrôle local , où il est facile d'éviter d'utiliser le branchement ordinaire
if - else
.la source
En règle générale, il n'y a rien de mal, en soi, à gérer une exception à un niveau bas. Une exception EST un message valide qui fournit beaucoup de détails sur les raisons pour lesquelles une opération ne peut pas être effectuée. Et si vous pouvez gérer cela, vous devriez le faire.
En général, si vous savez qu'il y a une forte probabilité d'échec que vous pouvez vérifier ... vous devriez faire la vérification ... c'est-à-dire si (obj! = Null) obj.method ()
Dans votre cas, je ne suis pas assez familier avec la bibliothèque C # pour savoir si la date et l'heure ont un moyen facile de vérifier si un horodatage est hors limites. Si c'est le cas, appelez simplement if (.isvalid (ts)) sinon votre code est fondamentalement correct.
Donc, fondamentalement, cela revient à la manière dont crée un code plus propre ... si l'opération de protection contre une exception attendue est plus complexe que la simple gestion de l'exception; que vous avez ma permission de gérer l'exception au lieu de créer des gardes complexes partout.
la source
Si vous utilisez des gestionnaires d'exceptions pour le flux de contrôle, vous êtes trop général et paresseux. Comme quelqu'un l'a mentionné, vous savez que quelque chose s'est passé si vous gérez le traitement dans le gestionnaire, mais que se passe-t-il exactement? Vous utilisez essentiellement l'exception pour une instruction else, si vous l'utilisez pour le flux de contrôle.
Si vous ne savez pas quel état possible pourrait se produire, vous pouvez utiliser un gestionnaire d'exceptions pour les états inattendus, par exemple lorsque vous devez utiliser une bibliothèque tierce, ou vous devez tout attraper dans l'interface utilisateur pour afficher une belle erreur message et enregistrez l'exception.
Cependant, si vous savez ce qui pourrait mal tourner et que vous ne mettez pas de déclaration if ou quelque chose pour le vérifier, alors vous êtes simplement paresseux. Permettre au gestionnaire d'exceptions d'être le fourre-tout pour les choses que vous savez que cela pourrait arriver est paresseux, et il reviendra vous hanter plus tard, car vous essaierez de corriger une situation dans votre gestionnaire d'exceptions sur la base d'une hypothèse éventuellement fausse.
Si vous mettez de la logique dans votre gestionnaire d'exceptions pour déterminer ce qui s'est exactement passé, vous seriez assez stupide de ne pas mettre cette logique dans le bloc try.
Les gestionnaires d'exceptions sont le dernier recours, lorsque vous manquez d'idées / de moyens pour empêcher quelque chose de mal tourner ou que les choses sont hors de votre capacité à contrôler. Par exemple, le serveur est en panne et expire et vous ne pouvez pas empêcher cette exception d'être levée.
Enfin, avoir toutes les vérifications effectuées à l'avance montre ce que vous savez ou prévoyez se produira et le rend explicite. Le code doit être clair dans l'intention. Que préférez-vous lire?
la source
Vous pourriez être intéressé à jeter un œil au système de conditions de Common Lisp qui est une sorte de généralisation des exceptions bien faites. Parce que vous pouvez dérouler la pile ou non de manière contrôlée, vous obtenez également des "redémarrages", qui sont extrêmement pratiques.
Cela n'a rien à voir avec les meilleures pratiques dans d'autres langues, mais cela vous montre ce qui peut être fait avec une certaine conception pensée dans (à peu près) la direction à laquelle vous pensez.
Bien sûr, il y a encore des considérations de performances si vous rebondissez de haut en bas dans la pile comme un yo-yo, mais c'est une idée beaucoup plus générale que l'approche «oh merde, laisse tomber» que la plupart des systèmes d'exception catch / throw incarnent.
la source
Je ne pense pas qu'il y ait quelque chose de mal à utiliser des exceptions pour le contrôle de flux. Les exceptions sont quelque peu similaires aux continuations et dans les langages à typage statique, les exceptions sont plus puissantes que les continuations, donc, si vous avez besoin de continuations mais que votre langage ne les a pas, vous pouvez utiliser des exceptions pour les implémenter.
Eh bien, en fait, si vous avez besoin de suites et que votre langue ne les a pas, vous avez choisi la mauvaise langue et vous devriez plutôt en utiliser une autre. Mais parfois, vous n'avez pas le choix: la programmation Web côté client est le meilleur exemple - il n'y a tout simplement aucun moyen de contourner JavaScript.
Un exemple: Microsoft Volta est un projet permettant d'écrire des applications Web dans .NET simple et de laisser le framework se charger de déterminer quels bits doivent s'exécuter où. Une conséquence de ceci est que Volta doit être capable de compiler CIL en JavaScript, afin que vous puissiez exécuter du code sur le client. Cependant, il y a un problème: .NET a le multithreading, pas JavaScript. Ainsi, Volta implémente des continuations en JavaScript à l'aide d'exceptions JavaScript, puis implémente les threads .NET en utilisant ces continuations. De cette façon, les applications Volta qui utilisent des threads peuvent être compilées pour s'exécuter dans un navigateur non modifié - aucun Silverlight n'est nécessaire.
la source
Une raison esthétique:
Un essai est toujours accompagné d'un crochet, alors qu'un if ne doit pas nécessairement être accompagné d'un else.
Avec try / catch, cela devient beaucoup plus verbeux.
Cela fait 6 lignes de code de trop.
la source
Mais vous ne saurez pas toujours ce qui se passe dans la ou les méthodes que vous appelez. Vous ne saurez pas exactement où l'exception a été lancée. Sans examiner l'objet d'exception plus en détail ...
la source
Je pense qu'il n'y a rien de mal avec votre exemple. Au contraire, ce serait un péché d'ignorer l'exception lancée par la fonction appelée.
Dans la JVM, lancer une exception n'est pas si cher, ne créer que l'exception avec la nouvelle xyzException (...), car cette dernière implique un parcours de pile. Donc, si vous avez créé des exceptions à l'avance, vous pouvez les lancer plusieurs fois sans frais. Bien sûr, de cette façon, vous ne pouvez pas transmettre de données avec l'exception, mais je pense que c'est une mauvaise chose à faire de toute façon.
la source
Il existe quelques mécanismes généraux par lesquels un langage pourrait permettre à une méthode de quitter sans renvoyer une valeur et de se dérouler au bloc "catch" suivant:
Demandez à la méthode d'examiner le cadre de la pile pour déterminer le site d'appel et d'utiliser les métadonnées du site d'appel pour trouver soit des informations sur un
try
bloc dans la méthode appelante, soit l'emplacement où la méthode appelante a stocké l'adresse de son appelant; dans ce dernier cas, examinez les métadonnées que l'appelant de l'appelant doit déterminer de la même manière que l'appelant immédiat, en répétant jusqu'à ce que l'on trouve untry
bloc ou que la pile soit vide. Cette approche ajoute très peu de surcharge au cas sans exception (elle exclut certaines optimisations) mais est coûteuse lorsqu'une exception se produit.Demandez à la méthode de renvoyer un indicateur "caché" qui distingue un retour normal d'une exception, et demandez à l'appelant de vérifier cet indicateur et de se connecter à une routine "d'exception" si elle est définie. Cette routine ajoute 1 à 2 instructions au cas sans exception, mais relativement peu de temps système lorsqu'une exception se produit.
Demandez à l'appelant de placer les informations ou le code de gestion des exceptions à une adresse fixe par rapport à l'adresse de retour empilée. Par exemple, avec l'ARM, au lieu d'utiliser l'instruction "BL subroutine", on pourrait utiliser la séquence:
Pour quitter normalement, le sous-programme ferait simplement
bx lr
oupop {pc}
; en cas de sortie anormale, le sous-programme soustraire 4 à LR avant d'effectuer le retour ou d'utilisersub lr,#4,pc
(selon la variation ARM, le mode d'exécution, etc.) Cette approche ne fonctionnera pas très mal si l'appelant n'est pas conçu pour l'accueillir.Un langage ou un cadre qui utilise des exceptions vérifiées pourrait bénéficier de la gestion de celles-ci avec un mécanisme comme # 2 ou # 3 ci-dessus, tandis que les exceptions non vérifiées sont gérées en utilisant # 1. Bien que la mise en œuvre d'exceptions vérifiées en Java soit plutôt gênante, ce ne serait pas un mauvais concept s'il y avait un moyen par lequel un site d'appel pourrait dire, essentiellement, "Cette méthode est déclarée comme lançant XX, mais je ne m'y attend pas jamais à le faire; si c'est le cas, renvoyer comme une exception "non vérifiée". Dans un cadre où les exceptions vérifiées étaient gérées de cette manière, elles pourraient être un moyen efficace de contrôle de flux pour des choses comme l'analyse des méthodes qui, dans certains contextes, peuvent avoir un forte probabilité d'échec, mais où l'échec devrait renvoyer des informations fondamentalement différentes de celles du succès. Cependant, je ne connais aucun framework utilisant un tel modèle. Au lieu de cela, le modèle le plus courant consiste à utiliser la première approche ci-dessus (coût minimal pour le cas sans exception, mais coût élevé lorsque des exceptions sont levées) pour toutes les exceptions.
la source