Il est déconseillé de simplement attraper System.Exception
. Au lieu de cela, seules les exceptions "connues" doivent être interceptées.
Maintenant, cela conduit parfois à un code répétitif inutile, par exemple:
try
{
WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
WebId = Guid.Empty;
}
catch (OverflowException)
{
WebId = Guid.Empty;
}
Je me demande: existe-t-il un moyen d'attraper les deux exceptions et de ne l' WebId = Guid.Empty
appeler qu'une seule fois?
L'exemple donné est assez simple, car il ne s'agit que d'un GUID
. Mais imaginez le code où vous modifiez un objet plusieurs fois, et si l'une des manipulations échoue de la manière attendue, vous voulez "réinitialiser" le object
. Cependant, s'il y a une exception inattendue, je veux toujours la jeter plus haut.
c#
.net
exception
exception-handling
Michael Stum
la source
la source
AggregateException
: Lancer une AggregateException dans mon propre codeRéponses:
Attrapez
System.Exception
et allumez les typesla source
EDIT: Je suis d'accord avec d'autres qui disent que, depuis C # 6.0, les filtres d'exception sont désormais une solution parfaitement adaptée:
catch (Exception ex) when (ex is ... || ex is ... )
Sauf que je déteste toujours la mise en page sur une seule ligne et que je présenterais personnellement le code comme suit. Je pense que c'est aussi fonctionnel qu'esthétique, car je pense que cela améliore la compréhension. Certains peuvent être en désaccord:
ORIGINAL:
Je sais que je suis un peu en retard à la fête ici, mais sainte fumée ...
Aller droit au but, ce type de duplique une réponse antérieure, mais si vous voulez vraiment effectuer une action commune pour plusieurs types d'exceptions et garder le tout propre et bien rangé dans le cadre de la seule méthode, pourquoi ne pas simplement utiliser un lambda / fermeture / fonction en ligne pour faire quelque chose comme ce qui suit? Je veux dire, les chances sont assez bonnes que vous finissiez par réaliser que vous voulez simplement faire de cette fermeture une méthode distincte que vous pouvez utiliser partout. Mais alors il sera super facile de le faire sans réellement changer le reste du code structurellement. Droite?
Je ne peux pas m'empêcher de me demander ( avertissement: un peu d'ironie / sarcasme à venir) pourquoi diable faire tous ces efforts pour remplacer simplement ce qui suit:
... avec une variation folle de cette odeur de code suivante, je veux dire par exemple, seulement pour prétendre que vous enregistrez quelques touches.
Parce qu'il n'est certainement pas automatiquement plus lisible.
Certes, j'ai laissé les trois instances identiques
/* write to a log, whatever... */ return;
du premier exemple.Mais c'est en quelque sorte mon point. Vous avez tous entendu parler des fonctions / méthodes, non? Sérieusement. Écrivez une
ErrorHandler
fonction commune et, comme, appelez-la depuis chaque bloc catch.Si vous me demandez, le deuxième exemple (avec les mots
if
-is
clés et ) est à la fois beaucoup moins lisible et simultanément beaucoup plus sujet aux erreurs pendant la phase de maintenance de votre projet.La phase de maintenance, pour toute personne qui pourrait être relativement nouvelle dans la programmation, représentera 98,7% ou plus de la durée de vie globale de votre projet, et le pauvre schmuck qui effectue la maintenance va presque certainement être quelqu'un d'autre que vous. Et il y a de fortes chances qu'ils passent 50% de leur temps au travail à maudire votre nom.
Et bien sûr, FxCop vous aboie et vous devez donc également ajouter un attribut à votre code qui a précisément un lien avec le programme en cours d'exécution, et n'est là que pour dire à FxCop d'ignorer un problème qui, dans 99,9% des cas, est totalement correct en signalant. Et, désolé, je peux me tromper, mais cet attribut "ignorer" ne se compile-t-il pas réellement dans votre application?
Est-ce que mettre l'ensemble du
if
test sur une seule ligne le rendrait plus lisible? Je ne pense pas. Je veux dire, il y a longtemps, un autre programmeur a soutenu avec véhémence que mettre plus de code sur une ligne le rendrait "plus rapide". Mais bien sûr, il était complètement fou. Essayer de lui expliquer (avec un visage impassible - ce qui était difficile) comment l'interprète ou le compilateur briserait cette longue ligne en déclarations discrètes d'une instruction par ligne - essentiellement identiques au résultat s'il était allé de l'avant et vient de rendre le code lisible au lieu d'essayer de surpasser le compilateur - n'a eu aucun effet sur lui. Mais je m'égare.À quel point cela est-il moins lisible lorsque vous ajoutez trois autres types d'exception, dans un mois ou deux? (Réponse: ça devient beaucoup moins lisible).
L'un des points majeurs, en réalité, est que la plupart du point de formatage du code source textuel que nous regardons tous tous les jours est de rendre vraiment, vraiment évident pour les autres êtres humains ce qui se passe réellement lorsque le code s'exécute. Parce que le compilateur transforme le code source en quelque chose de totalement différent et ne se soucie pas du style de formatage de votre code. Donc, tout-en-une-ligne est tout aussi nul.
Je dis juste ...
la source
Exception
s et vérifier le type. Je pensais que cela nettoyait le code, mais quelque chose me faisait revenir à la question et j'ai lu les autres réponses à la question. Je l'ai mâchée pendant un moment, mais je dois être d'accord avec vous. Il est plus lisible et maintenable d'utiliser une fonction pour sécher votre code que pour tout récupérer, vérifier le type en comparant avec une liste, en encapsulant le code et en lançant. Merci d'être venu en retard et d'avoir fourni une option alternative et saine (IMO). +1.throw;
. Il faudrait répéter cette ligne de code dans chaque bloc catch (évidemment pas la fin du monde mais mérite d'être mentionné car c'est du code qui devrait être répété).throw();
instruction supplémentaire dans chaque bloc de capture est un petit prix à payer, OMI, et vous laisse toujours en mesure de faire un nettoyage supplémentaire spécifique au type d'exception si nécessaire.Func<Exception, MyEnumType>
au lieu deAction<Exception>
. C'estFunc<T, Result>
, avecResult
le type de retour.Comme d'autres l'ont souligné, vous pouvez avoir une
if
déclaration à l'intérieur de votre bloc catch pour déterminer ce qui se passe. C # 6 prend en charge les filtres d'exception, donc les éléments suivants fonctionneront:La
MyFilter
méthode pourrait alors ressembler à ceci:Alternativement, cela peut être fait en ligne (le côté droit de l'instruction when doit simplement être une expression booléenne).
Ceci est différent de l'utilisation d'une
if
instruction dans lecatch
bloc, l'utilisation de filtres d'exception ne déroulera pas la pile.Vous pouvez télécharger Visual Studio 2015 pour vérifier cela.
Si vous souhaitez continuer à utiliser Visual Studio 2013, vous pouvez installer le package de nuget suivant:
Au moment de la rédaction de ce document, cela inclura la prise en charge de C # 6.
la source
Pas en C # malheureusement, car vous auriez besoin d'un filtre d'exception pour le faire et C # n'expose pas cette fonctionnalité de MSIL. VB.NET a cependant cette capacité, par exemple
Ce que vous pourriez faire, c'est utiliser une fonction anonyme pour encapsuler votre code d'erreur, puis l'appeler dans ces blocs catch spécifiques:
la source
throw e;
détruit stacktrace et callstack,throw;
détruit "uniquement" callstack (rendant les vidages sur incident inutiles!) Une très bonne raison de ne pas utiliser l'un ou l'autre si cela peut être évité!Par souci d'exhaustivité, depuis .NET 4.0, le code peut être réécrit comme suit:
TryParse ne lève jamais d'exceptions et renvoie false si le format est incorrect, en définissant WebId sur
Guid.Empty
.Depuis C # 7, vous pouvez éviter d'introduire une variable sur une ligne distincte:
Vous pouvez également créer des méthodes pour analyser les tuples de retour, qui ne sont pas encore disponibles dans .NET Framework à partir de la version 4.6:
Et utilisez-les comme ceci:
La prochaine mise à jour inutile de cette réponse inutile survient lorsque la déconstruction des paramètres externes est implémentée en C # 12. :)
la source
Guid.TryParse
ne revient jamaisGuid.Empty
. Si la chaîne est dans un format incorrect, elle définit leresult
paramètre de sortie surGuid.Empty
, mais elle revientfalse
. Je le mentionne parce que j'ai vu du code qui fait les choses dans le style deGuid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }
, ce qui est généralement faux s'ils
pourrait s'agir de la représentation sous forme de chaîne deGuid.Empty
.if( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }
ce qui ne laisse aucune ambiguïté comme l'exemple cassé où la valeur d'entrée pourrait en fait être la représentation sous forme de chaîne d'un Guid.Les filtres d'exception sont désormais disponibles dans c # 6+. Tu peux faire
Dans C # 7.0+, vous pouvez également combiner cela avec la correspondance de motifs
la source
Si vous pouvez mettre à jour votre application en C # 6, vous avez de la chance. La nouvelle version C # a implémenté des filtres d'exception. Vous pouvez donc écrire ceci:
Certaines personnes pensent que ce code est le même que
Mais ce n'est pas. En fait, c'est la seule nouvelle fonctionnalité de C # 6 qui n'est pas possible d'émuler dans les versions précédentes. Tout d'abord, une relance signifie plus de frais généraux que de sauter le crochet. Deuxièmement, il n'est pas sémantiquement équivalent. La nouvelle fonctionnalité conserve la pile intacte lorsque vous déboguez votre code. Sans cette fonctionnalité, le vidage sur incident est moins utile, voire inutile.
Voir une discussion à ce sujet sur CodePlex . Et un exemple montrant la différence .
la source
Si vous ne souhaitez pas utiliser un
if
déclaration dans lescatch
champs, enC# 6.0
vous pouvez utiliser laException Filters
syntaxe qui a déjà été pris en charge par le CLR dans les versions des avant - premières , mais n'a existé que dansVB.NET
/MSIL
:Ce code n'attrapera
Exception
que lorsqu'il s'agit d'unInvalidDataException
ouArgumentNullException
.En fait, vous pouvez mettre pratiquement n'importe quelle condition à l'intérieur de cette
when
clause:Notez que contrairement à un
if
instruction à l'intérieur de lacatch
portée de,Exception Filters
ne peut pas lancerExceptions
, et quand ils le font, ou lorsque la condition ne l'est pastrue
, lacatch
condition suivante sera évaluée à la place:Lorsqu'il y en a plus d'un
true
Exception Filter
- le premier sera accepté:Et comme vous pouvez le voir dans le
MSIL
code, le code n'est pas traduit enif
instructions, mais enFilters
etExceptions
ne peut pas être jeté à partir des zones marquées parFilter 1
etFilter 2
mais le filtre lançant leException
échouera à la place, également la dernière valeur de comparaison poussée dans la pile avant laendfilter
commande déterminera le succès / l'échec du filtre (Catch 1
XORCatch 2
s'exécutera en conséquence):Aussi, a spécifiquement
Guid
laGuid.TryParse
méthode.la source
Avec C # 7, la réponse de Michael Stum peut être améliorée tout en conservant la lisibilité d'une instruction switch:
Et avec C # 8 comme expression de commutateur:
la source
when
est beaucoup plus élégante / appropriée qu'un interrupteur.catch
bloc , ou vous devrez quand même le caster .. D'après mon expérience, unthrow;
dans votrecatch
bloc est probablement une odeur de code.La réponse acceptée semble acceptable, sauf que CodeAnalysis / FxCop se plaindra du fait qu'il un type d'exception générale.
De plus, il semble que l'opérateur "is" puisse dégrader légèrement les performances.
CA1800: Ne jetez pas inutilement dit de "envisager de tester le résultat de l'opérateur" as "à la place", mais si vous le faites, vous écrirez plus de code que si vous interceptez chaque exception séparément.
Quoi qu'il en soit, voici ce que je ferais:
la source
is
opérateur peut avoir un léger impact négatif sur les performances, il est également vrai qu'un gestionnaire d'exceptions n'est pas le lieu d'être trop préoccupé par l'optimisation des performances. Si votre application passe tellement de temps dans les gestionnaires d'exceptions que l'optimisation des performances ferait une réelle différence dans les performances de l'application, il y a d'autres problèmes de code à examiner attentivement. Cela dit, je n'aime toujours pas cette solution car vous perdez la trace de la pile et parce que le nettoyage est contextuellement supprimé de l'instruction catch.is
opérateur dégrade les performances est si vous effectuez uneas
opération ultérieurement (d'où ils qualifient la règle inutilement ). Si tout ce que vous faites est de tester le plâtre sans avoir réellement besoin de le faire, alors l'is
opérateur est exactement ce que vous voulez utiliser.en C # 6 l'approche recommandée est d'utiliser des filtres d'exception, voici un exemple:
la source
Ceci est une variante de la réponse de Matt (je pense que c'est un peu plus propre) ... utilisez une méthode:
Toute autre exception sera levée et le code
WebId = Guid.Empty;
ne sera pas touché. Si vous ne voulez pas que d'autres exceptions plantent votre programme, ajoutez simplement ceci APRÈS les deux autres captures:la source
WebId = Guid.Emtpy
dans le cas où aucune exception n'a été levée.return
à ma réponse. Merci pour la contribution.La réponse de Joseph Daigle est une bonne solution, mais j'ai trouvé que la structure suivante était un peu plus ordonnée et moins sujette aux erreurs.
Il y a quelques avantages à inverser l'expression:
Il peut même être compacté en une seule ligne (mais pas très joli)
Edit: Le filtrage d'exceptions en C # 6.0 rendra la syntaxe un peu plus propre et offre un certain nombre d'autres avantages rapport à toute solution actuelle. (en laissant notamment la pile indemne)
Voici à quoi ressemblerait le même problème en utilisant la syntaxe C # 6.0:
la source
return
, bien que l'inversion de la condition soit également un peu meilleure.@Micheal
Version légèrement révisée de votre code:
Les comparaisons de chaînes sont laides et lentes.
la source
throw new FormatException();
parthrow new NewlyDerivedFromFormatException();
sans casser le code en utilisant la bibliothèque, et cela restera vrai pour tous les cas de gestion des exceptions, sauf lorsque quelqu'un l'a utilisé à la==
place deis
(ou simplementcatch (FormatException)
).Que diriez-vous
la source
la source
Attention et averti: Encore un autre style fonctionnel et aimable.
Ce qui est dans le lien ne répond pas directement à votre question, mais il est trivial de l'étendre pour ressembler à:
(Fournir essentiellement une autre
Catch
surcharge vide qui se retourne)La plus grande question à cela est pourquoi . Je ne pense pas que le coût l'emporte sur le gain ici :)
la source
Mise à jour 2015-12-15: voir https://stackoverflow.com/a/22864936/1718702 pour C # 6. C'est un nettoyeur et désormais standard dans la langue.
Destiné aux personnes qui souhaitent une solution plus élégante pour intercepter une fois et filtrer les exceptions, j'utilise une méthode d'extension comme indiqué ci-dessous.
J'avais déjà cette extension dans ma bibliothèque, écrite à l'origine à d'autres fins, mais elle fonctionnait parfaitement pour
type
vérifier les exceptions. De plus, à mon humble avis, il semble plus propre qu'un tas de||
déclarations. De plus, contrairement à la réponse acceptée, je préfère la gestion des exceptions explicites, donc leex is ...
comportement était indésirable car les classes dérivées sont attribuables aux types parents).Usage
Extension IsAnyOf.cs (Voir l'exemple complet de gestion des erreurs pour les dépendances)
Exemple de gestion complète des erreurs (copier-coller dans la nouvelle application console)
Deux exemples de tests unitaires NUnit
Le comportement de correspondance pour les
Exception
types est exact (c'est-à-dire qu'un enfant N'EST PAS une correspondance pour aucun de ses types parents).la source
when
, comme le ferait n'importe quelle version decatch (Exception ex) {if (...) {/*handle*/} throw;}
. La valeur réelle dewhen
est que le filtre s'exécute avant que l'exception ne soit interceptée , évitant ainsi la corruption de dépenses / pile d'un re-throw. Il tire parti d'une fonctionnalité CLR qui n'était auparavant accessible qu'à VB et MSIL.IsAnyOf
méthode peut être réécrite simplementp_comparisons.Contains(p_parameter)
Comme j'avais l'impression que ces réponses touchaient la surface, j'ai essayé de creuser un peu plus profondément.
Donc, ce que nous voudrions vraiment faire, c'est quelque chose qui ne compile pas, disons:
La raison pour laquelle nous voulons cela est parce que nous ne voulons pas que le gestionnaire d'exceptions intercepte les éléments dont nous avons besoin plus tard dans le processus. Bien sûr, nous pouvons attraper une exception et vérifier avec un «si» quoi faire, mais soyons honnêtes, nous ne voulons pas vraiment cela. (FxCop, problèmes de débogage, laideur)
Alors pourquoi ce code ne compile-t-il pas - et comment pouvons-nous le pirater de telle manière qu'il le fera?
Si nous regardons le code, ce que nous aimerions vraiment faire, c'est transférer l'appel. Cependant, selon MS Partition II, les blocs de gestionnaire d'exceptions IL ne fonctionneront pas comme cela, ce qui dans ce cas est logique car cela impliquerait que l'objet «exception» peut avoir différents types.
Ou pour l'écrire dans le code, nous demandons au compilateur de faire quelque chose comme ça (enfin ce n'est pas tout à fait correct, mais c'est la chose la plus proche possible je suppose):
La raison pour laquelle cela ne se compile pas est tout à fait évidente: quel type et quelle valeur aurait l'objet '$ exception' (qui sont ici stockés dans les variables 'e')? La façon dont nous voulons que le compilateur gère cela est de noter que le type de base commun des deux exceptions est «Exception», utilisez-le pour qu'une variable contienne les deux exceptions, puis ne gérez que les deux exceptions qui sont interceptées. La façon dont cela est implémenté dans IL est comme un «filtre», qui est disponible dans VB.Net.
Pour le faire fonctionner en C #, nous avons besoin d'une variable temporaire avec le type de base 'Exception' correct. Pour contrôler le flux du code, nous pouvons ajouter quelques branches. Voici:
Les inconvénients évidents pour cela sont que nous ne pouvons pas relancer correctement, et - soyons honnêtes - c'est tout à fait la solution laide. La laideur peut être corrigée un peu en effectuant l'élimination des branches, ce qui rend la solution légèrement meilleure:
Cela ne laisse que le «re-throw». Pour que cela fonctionne, nous devons être en mesure d'effectuer la gestion à l'intérieur du bloc «catch» - et la seule façon de faire fonctionner cela est par un objet «Exception» de capture.
À ce stade, nous pouvons ajouter une fonction distincte qui gère les différents types d'exceptions à l'aide de la résolution de surcharge ou pour gérer l'exception. Les deux ont des inconvénients. Pour commencer, voici la façon de le faire avec une fonction d'assistance:
Et l'autre solution consiste à intercepter l'objet Exception et à le gérer en conséquence. La traduction la plus littérale pour cela, basée sur le contexte ci-dessus est la suivante:
Donc pour conclure:
la source
C'est un problème classique auquel tous les développeurs C # sont confrontés.
Permettez-moi de diviser votre question en 2 questions. La première,
Puis-je intercepter plusieurs exceptions à la fois?
Bref, non.
Ce qui nous amène à la question suivante,
Comment éviter d'écrire du code en double étant donné que je ne peux pas intercepter plusieurs types d'exceptions dans le même bloc catch ()?
Compte tenu de votre échantillon spécifique, où la valeur de secours est bon marché à construire, j'aime suivre ces étapes:
Le code ressemble donc à:
Si une exception est levée, WebId n'est jamais défini sur la valeur semi-construite et reste Guid.Empty.
Si la construction de la valeur de secours est coûteuse et que la réinitialisation d'une valeur est beaucoup moins chère, je déplacerais le code de réinitialisation dans sa propre fonction:
la source
Vous répétez donc beaucoup de code dans chaque commutateur d'exception? On dirait que l'extraction d'une méthode serait une idée divine, n'est-ce pas?
Donc, votre code se résume à ceci:
Je me demande pourquoi personne n'a remarqué cette duplication de code.
De C # 6, vous avez en outre les filtres d'exception comme déjà mentionné par d'autres. Vous pouvez donc modifier le code ci-dessus en ceci:
la source
Je voulais ajouter ma réponse courte à ce fil déjà long. Quelque chose qui n'a pas été mentionné est l'ordre de priorité des instructions catch, plus précisément vous devez être conscient de la portée de chaque type d'exception que vous essayez d'attraper.
Par exemple , si vous utilisez un « fourre-tout » exception comme exception il précédera toutes les autres déclarations de captures et vous aurez évidemment obtenir des erreurs compilateur si vous inversez l'ordre que vous pouvez enchaîner vos déclarations de captures (peu d'un anti-modèle que je pense ), vous pouvez mettre le type d' exception fourre-tout en bas et ce sera capturer toutes les exceptions qui ne répondaient pas plus haut dans votre bloc try..catch:
Je recommande fortement aux gens de lire ce document MSDN:
Hiérarchie des exceptions
la source
Peut-être essayez-vous de garder votre code simple, par exemple en mettant le code commun dans une méthode, comme vous le feriez dans toute autre partie du code qui ne se trouve pas dans une clause catch?
Par exemple:
Juste comment je le ferais, en essayant de trouver le modèle simple et magnifique
la source
Notez que j'ai trouvé une façon de le faire, mais cela ressemble plus à du matériel pour The Daily WTF :
la source
Il convient de mentionner ici. Vous pouvez répondre aux multiples combinaisons (erreur d'exception et message d'exception).
J'ai rencontré un scénario de cas d'utilisation en essayant de caster un objet de contrôle dans une grille de données, avec un contenu comme TextBox, TextBlock ou CheckBox. Dans ce cas, l'exception renvoyée était la même, mais le message variait.
la source
Je veux suggérer la réponse la plus courte (un style plus fonctionnel ):
Pour cela, vous devez créer plusieurs surcharges de méthode "Catch", similaires à System.Action:
et ainsi de suite autant que vous le souhaitez. Mais vous devez le faire une fois et vous pouvez l'utiliser dans tous vos projets (ou, si vous avez créé un paquet nuget, nous pourrions également l'utiliser).
Et la mise en œuvre de CatchMany:
ps Je n'ai pas mis de vérification nulle pour la simplicité du code, pensez à ajouter des validations de paramètres.
ps2 Si vous souhaitez renvoyer une valeur de catch, il est nécessaire de faire les mêmes méthodes Catch, mais avec des retours et Func au lieu d'Action dans les paramètres.
la source
Il suffit d'appeler l'essai et d'attraper deux fois.
C'est aussi simple que ça !!
la source
Dans c # 6.0, les filtres d'exception sont des améliorations pour la gestion des exceptions
la source
catch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; }