Prenons une situation dans laquelle j'ai trois (ou plus) façons d'effectuer un calcul, chacune pouvant échouer avec une exception. Afin d'essayer chaque calcul jusqu'à ce que nous trouvions celui qui réussit, j'ai fait ce qui suit:
double val;
try { val = calc1(); }
catch (Calc1Exception e1)
{
try { val = calc2(); }
catch (Calc2Exception e2)
{
try { val = calc3(); }
catch (Calc3Exception e3)
{
throw new NoCalcsWorkedException();
}
}
}
Existe-t-il un modèle accepté qui permet d'atteindre cet objectif d'une manière plus agréable? Bien sûr, je pourrais envelopper chaque calcul dans une méthode d'assistance qui renvoie null en cas d'échec, puis utiliser simplement l' ??
opérateur, mais y a-t-il un moyen de le faire plus généralement (c'est-à-dire sans avoir à écrire une méthode d'assistance pour chaque méthode que je veux utiliser )? J'ai pensé à écrire une méthode statique utilisant des génériques qui encapsule une méthode donnée dans un try / catch et retourne null en cas d'échec, mais je ne suis pas sûr de la façon dont je procéderais. Des idées?
la source
String
enDate
utilisantSimpleDateFormat.parse
et je dois essayer plusieurs formats différents dans l'ordre, passant au suivant quand on lance une exception.Réponses:
Dans la mesure du possible, n'utilisez pas d'exceptions pour le flux de contrôle ou des circonstances non exceptionnelles.
Mais pour répondre directement à votre question (en supposant que tous les types d'exceptions sont identiques):
la source
Calc1Exception
,Calc2Exception
etCalc3Exception
partager une classe de base commune.continue
dans le bloc catch etbreak
après le bloc catch pour que la boucle se termine lorsqu'un calcul fonctionne (merci à Lirik pour ce bit)break
déclaration qui suitcalc();
dans letry
(et aucunecontinue
instruction du tout) pourrait être un peu plus idiomatique.Juste pour proposer une alternative "hors des sentiers battus", que diriez-vous d'une fonction récursive ...
REMARQUE: je ne dis en aucun cas que c'est la meilleure façon de faire le travail, juste une manière différente
la source
return DoCalc(c++)
équivaut àreturn DoCalc(c)
- la valeur post-incrémentée ne sera pas transmise plus profondément. Pour le faire fonctionner (et introduire plus d'obscurité), cela pourrait ressembler davantage àreturn DoCalc((c++,c))
.Vous pouvez aplatir l'imbrication en la mettant dans une méthode comme celle-ci:
Mais je soupçonne que le vrai problème de conception est l'existence de trois méthodes différentes qui font essentiellement la même chose (du point de vue de l'appelant) mais lancent des exceptions différentes et sans rapport.
Cela suppose que les trois exceptions ne sont pas liées. S'ils ont tous une classe de base commune, il serait préférable d'utiliser une boucle avec un seul bloc catch, comme l'a suggéré Ani.
la source
Essayez de ne pas contrôler la logique basée sur des exceptions; notez également que des exceptions ne devraient être levées que dans des cas exceptionnels. Dans la plupart des cas, les calculs ne doivent pas lever d'exceptions, sauf s'ils accèdent à des ressources externes ou analysent des chaînes ou quelque chose. Quoi qu'il en soit, dans le pire des cas, suivez le style TryMethod (comme TryParse ()) pour encapsuler la logique d'exception et rendre votre flux de contrôle maintenable et propre:
Une autre variante utilisant le modèle Try et combinant la liste de méthodes au lieu d'imbriquer si:
et si le foreach est un peu compliqué, vous pouvez le préciser:
la source
Créez une liste de délégués à vos fonctions de calcul, puis disposez d'une boucle while pour les parcourir:
Cela devrait vous donner une idée générale de la façon de le faire (c'est-à-dire que ce n'est pas une solution exacte).
la source
Exception
alors. ;)Exception
.Cela ressemble à un travail pour ... MONADS! Plus précisément, la monade peut-être. Commencez par la monade Maybe comme décrit ici . Ajoutez ensuite quelques méthodes d'extension. J'ai écrit ces méthodes d'extension spécifiquement pour le problème tel que vous l'avez décrit. La bonne chose à propos des monades est que vous pouvez écrire les méthodes d'extension exactes nécessaires à votre situation.
Utilisez-le comme ceci:
Si vous faites souvent ce genre de calculs, peut-être que la monade devrait réduire la quantité de code standard que vous devez écrire tout en augmentant la lisibilité de votre code.
la source
Maybe
n'est pas parce qu'il utilise une solution monadique; il utilise zéro des propriétés monadiques deMaybe
et peut donc tout aussi bien l'utilisernull
. En outre, utilisé "monadiquement", ce serait l' inverse duMaybe
. Une vraie solution monadique devrait utiliser une monade d'État qui conserve la première valeur non exceptionnelle comme état, mais ce serait exagéré lorsque l'évaluation en chaîne normale fonctionne.Une autre version de l' approche de la méthode try . Celui-ci autorise les exceptions typées, car il existe un type d'exception pour chaque calcul:
la source
&&
entre chaque condition.En Perl, vous pouvez faire
foo() or bar()
, qui s'exécutera enbar()
cas d'foo()
échec. En C #, nous ne voyons pas cette construction "en cas d'échec, alors", mais il existe un opérateur que nous pouvons utiliser à cette fin: l'opérateur null-coalesce??
, qui ne continue que si la première partie est nulle.Si vous pouvez modifier la signature de vos calculs et si vous encapsulez leurs exceptions (comme indiqué dans les articles précédents) ou les réécrivez pour les renvoyer à la
null
place, votre chaîne de code devient de plus en plus brève et toujours facile à lire:J'ai utilisé les remplacements suivants pour vos fonctions, ce qui aboutit à la valeur
40.40
enval
.Je me rends compte que cette solution ne sera pas toujours applicable, mais vous avez posé une question très intéressante et je crois, même si le fil est relativement ancien, qu’il s’agit d’un modèle à prendre en compte lorsque vous pouvez faire amende honorable.
la source
Étant donné que les méthodes de calcul ont la même signature sans paramètre, vous pouvez les enregistrer dans une liste, parcourir cette liste et exécuter les méthodes. Il serait probablement encore mieux pour vous d'utiliser le
Func<double>
sens "une fonction qui renvoie un résultat de typedouble
".la source
Vous pouvez utiliser un Task / ContinueWith et rechercher l'exception. Voici une belle méthode d'extension pour aider à la rendre jolie:
la source
Si le type réel de l'exception levée n'a pas d'importance, vous pouvez simplement utiliser un bloc catch sans type:
la source
if(succeeded) { break; }
post-catch.Et utilisez-le comme ceci:
la source
Il semble que l'intention du PO était de trouver un bon modèle pour résoudre son problème et résoudre le problème actuel avec lequel il luttait à ce moment-là.
J'ai vu beaucoup de bons modèles qui évitent les blocs try catch imbriqués , publiés dans ce flux, mais n'ai pas trouvé de solution au problème cité ci-dessus. Alors, voici la solution:
Comme OP mentionné ci-dessus, il voulait créer un objet wrapper qui retourne
null
en cas d'échec . Je l'appellerais un pod ( pod sans danger pour les exceptions ).Mais que faire si vous souhaitez créer un pod sécurisé pour un résultat de type de référence retourné par les fonctions / méthodes CalcN ().
Ainsi, vous remarquerez peut-être qu'il n'est pas nécessaire "d'écrire une méthode d'assistance pour chaque méthode que vous souhaitez utiliser" .
Les deux types de pods (pour
ValueTypeResult
s etReferenceTypeResult
s) suffisent .Voici le code de
SafePod
. Ce n'est cependant pas un conteneur. Au lieu de cela, il crée un wrapper de délégué sécurisé pour les exceptions pourValueTypeResult
s etReferenceTypeResult
s.C'est ainsi que vous pouvez tirer parti de l'opérateur de fusion nul
??
combiné à la puissance d' entités citoyennes de premier ordredelegate
.la source
Vous avez raison d'encapsuler chaque calcul, mais vous devez encapsuler selon le principe tell-don't-ask.
Les opérations sont simples et peuvent changer leur comportement de manière indépendante. Et peu importe pourquoi ils sont par défaut. Pour prouver, vous pouvez implémenter calc1DefaultingToCalc2 comme:
la source
Il semble que vos calculs contiennent plus d'informations valides à renvoyer que le calcul lui-même. Il serait peut-être plus judicieux pour eux de faire leur propre gestion des exceptions et de renvoyer une classe «results» contenant des informations d'erreur, des informations de valeur, etc. Pensez comme la classe AsyncResult suit le modèle async. Vous pouvez ensuite évaluer le résultat réel du calcul. Vous pouvez rationaliser cela en pensant que si un calcul échoue, c'est tout aussi informatif que s'il réussissait. Par conséquent, une exception est une information et non une «erreur».
Bien sûr, je m'interroge davantage sur l'architecture globale que vous utilisez pour effectuer ces calculs.
la source
while (!calcResult.HasValue) nextCalcResult()
, au lieu d'une liste de Calc1, Calc2, Calc3 etc.Qu'en est-il du suivi des actions que vous faites ...
la source
track
dans ce cas), et je sais exactement quel segment de mon code a causé l'échec du bloc. Peut-être auriez-vous dû élaborer pour dire à des membres comme DeCaf que letrack
message est envoyé à votre routine de gestion d'erreur personnalisée qui vous permet de déboguer votre code. On dirait qu'il n'a pas compris votre logique.