8
votes

Attraper des exceptions spécifiques vs génériques dans c #

Cette question provient d'une analyse de code exécutée contre un objet que j'ai créé. L'analyse indique que je devrais attraper un type d'exception plus spécifique que l'exception de base.

Recherchez-vous simplement de saisir l'exception générique ou de tenter d'attraper une exception spécifique et de la défaillance d'une exception générique à l'aide de plusieurs blocs de captures? < / p>

L'un des morceaux de code en question est ci-dessous: xxx

merci pour votre conseil

edit: Voici l'avertissement de l'analyse de code

Avertissement 351 CA1031: Microsoft.Design: Modifier 'ClearFlags (String, GUID)' pour attraper une exception plus précise que "exception" ou réthrow l'exception


0 commentaires

7 Réponses :


24
votes

Vous ne devriez presque jamais attraper l'exception de niveau supérieur.

Dans la plupart des cas, vous devez attraper et gérer l'exception la plus spécifique possible et uniquement s'il y a quelque chose d'utile que vous puissiez faire avec elle.

L'exception (HAHA) à ceci est si vous attrapez la journalisation et rejette à nouveau l'exception, il est parfois correct d'attraper une exception de niveau supérieur, de le loger et de son report.

Vous ne devriez presque jamais attraper une exception de niveau supérieur et l'avaler. C'est parce que si vous attrapez une exception de haut niveau, vous ne savez pas vraiment ce que vous manipulez; Absolument tout aurait pu le causer afin que vous ne puissiez certainement pas faire quelque chose qui gérera correctement chaque cas de panne. Il y a probablement des échecs que vous voudrez peut-être simplement manipuler et avaler silencieusement, mais simplement avaler des exceptions de niveau supérieur, vous avalerez également un groupe entier qui aurait vraiment dû être projeté vers le haut pour que votre code soit géré plus haut. Dans votre exemple exemple, ce que vous voulez probablement faire est de gérer une sqlexception et un journal + aval. Et puis pour une exception, log et retirent-le. Cela me recouvre. Vous enregistrez toujours tous les types d'exception, mais votre simple avaler la sqlexception assez prévisible qui indique des problèmes avec votre SQL / base de données.

Une pratique courante ne concerne que toutes les exceptions de manœuvre que vous pouvez réellement résoudre à ce stade, si vous ne pouvez pas le résoudre à ce moment-là, vous le permettez de la bulle vers le haut. Si vous ne pouvez pas le résoudre au niveau suivant, laissez-le continuer. S'il atteint le sommet non inondé, affichez une application polie à l'utilisateur (tente peut-être une touche automatique rapide) et fermez l'application. Il est généralement considéré comme pire pour permettre à une application de continuer à fonctionner après une exception non gérée, car vous ne pouvez pas prédire l'état de l'application car quelque chose d'exceptionnel s'est produit. Il vaut mieux juste arrêter et redémarrer l'application pour revenir à un état attendu.


8 commentaires

@Simon: merci! J'apprécie la réponse! Pouvez-vous me donner un peu plus sur la raison pour laquelle vous ne devriez pas attraper et avaler une exception de haut niveau. La manière dont j'ai mon code maintenant, la méthode "ErrorHandler" rassemble autant d'informations que possible à partir de l'exception et la cache dans le journal des événements (trace de pile, exception interne, message, etc.). Je ne suis pas sûr des conséquences d'attraper / traiter l'exception de votre point de vue.


Quel serait le point d'attraper une exception, de l'enregistrer, puis de le repousser?


@Kyratryessa: bas de votre pile de code dans les choses DAL pourrait vous tromper que vous souhaitez vous connecter.So vous attrapez et connectez-vous une exception de niveau supérieur, mais vous ne devriez pas l'avaler parce que vous ne savez pas vraiment ce qui s'est mal passé (et Vous risquez de avaler quelque chose de grave comme OutofMemex) .so vous devriez retirer.Si vous vouliez vous y gérer, vous devez saisir le type d'exception spécifique que vous savez à la place. Si tout ce que vous pouvez recourir à une "exception", vous devez retirer (ou envelopper et retirez-vous) pour permettre à un appeleur plus élevé pour le gérer de manière appropriée. @Scott: J'ai développé ma réponse un peu.


@Simon: Excellent! Merci, l'explication élargie est très appréciée et a aidé beaucoup.


Bien sûr, vous ne devriez pas l'avaler. Mon point est, pourquoi le loger si vous allez seulement retirer quand même? Vous devriez pouvoir utiliser la hiérarchie des exceptions pour capturer toutes les exceptions que vous Attendez-vous dans votre dal; Mais la journalisation des autres semble inutile car ils seront attrapés plus loin (après tout, vous les retirez), et si vous les reproduisez correctement, ils auront une pile d'appels complète pour indiquer où les choses se passaient mal.


(1) @KYRALESSA: Nous avons une longue application de serveur de course. Il est généralement de plus en plus de semaines à la fois. Dans cette durée de longueur, il y aura bien entendu des délais d'attente de base de données occasionnels. Nous les logons et les jetons de la DAL. Selon ce qui demandait que les données dépendent de la manière dont ils sont traités de plus en plus. Parfois, s'il s'agissait simplement d'une actualisation du serveur chronométré de ses données, cela ignore simplement silencieusement la défaillance (bien elle compte en fait les défaillances et plusieurs défaillances d'une ligne génère une erreur et une notification plus graves),


(2) Mais parfois, s'il s'agissait d'une demande d'utilisateur spécifique de données, il est nécessaire de les informer de l'échec. Parce que de la DAL, vous ne savez pas quel type d'appelant a demandé aux données que nous préférons de toujours le connecter au niveau inférieur. À la BLL, nous n'avons alors pas à vous en soucier parce que nous savons que toutes les erreurs DAL sont enregistrées. Nous vous connectons ensuite uniquement au BLL s'il y a des informations supplémentaires à fournir, telles que la source de la demande de données.


(3) J'imagine également que la journalisation à chaque niveau serait utile si vous essayez de déboguer la manipulation des erreurs ou d'avoir une obligation d'instrumentation de traçage détaillé. Cela revient à des exigences spécifiques que je suppose. Oui Dans une application client de base, vous ne voudriez probablement pas me connecter ni retire à aucun niveau, mais dans une application de serveur à plusieurs niveaux, la distinction entre les couches peut être importante, notamment lors de la traçage du chemin d'exécution des erreurs. (Woah, désolé pour les messages massifs).



1
votes

oui,

Vous devriez prendre de l'exception la moins spécifique au moins, afin que vous puissiez traiter des choses de manière appropriée.

Par exemple, si vous faites une demande Web, vous devez d'abord attraper des éléments tels que les délais d'expiration et 404, vous pouvez en informer l'utilisateur final, ils devraient réessayer (délai d'attente) et / ou vérifier leur URL sont entrées.

Ensuite, vous pourriez attraper quelque chose de moins général, au cas où quelque chose d'un peu plus rawacky se passe mal, puis tombe à juste titre à attraper une exception dans le cas où quelque chose de ridicule se produit.


0 commentaires

1
votes

comme une meilleure pratique, vous devriez éviter d'attraper exception et d'utiliser des indicateurs comme des valeurs de retour.

Au lieu de cela, vous devez concevoir des exceptions personnalisées pour les exceptions attendues et les attraper directement. Autre chose devrait bouillir comme une exception inattendue.

Dans votre exemple ci-dessus, vous voudrez peut-être réthorner une exception spécifique plus professionnelle.


0 commentaires

1
votes

Vous devriez lire un document général ou Google "Manipulation de l'exception structurée" et obtenir une meilleure image de grande taille de ce que ce sujet est tout à propos, mais en général, la capture de toutes les exceptions est considérée comme une mauvaise pratique car vous n'avez aucune idée de l'exception (Défaut de mémoire, erreur de mémoire, échec du disque, etc.).

et pour de nombreuses exceptions inconnues / inattendues, vous ne devez pas laisser la demande de continuer. En général, vous «attrapez» et gérez uniquement ces exceptions que jouet ont déterminé, à la suite d'une analyse de la méthode que vous codez la clause de capture, cette méthode peut en fait créer et que vous pouvez faire quelque chose à propos de. La seule fois que vous devriez attraper toutes les exceptions (une exception de capture X) est de faire quelque chose comme la journalisation, auquel cas vous devez immédiatement repenser la même exception (quoi que ce soit) afin de pouvoir bouillir la pile à une certaine exception générale "non gérée Hangler "qui peut afficher un message approprié à l'utilisateur, puis provoquer la terminaison de l'application.


0 commentaires

0
votes

Je suis d'accord avec l'outil d'analyse de code. Mon exception à la règle est que j'attache l'exception générale dans mes manutentionnaires d'événement et que l'utilisateur reçoit la possibilité de signaler l'erreur ou de l'ignorer.

Dans l'exemple que vous fournissez, je pense que l'analyse du code a bien compris. Si vous ne pouvez pas gérer une exception spécifique là-bas, vous ne devriez rien attraper du tout et laissez-la sous la bulle jusqu'au niveau le plus élevé. De cette façon, vous aurez beaucoup plus facile de recréer la question lorsque vous essayez de le réparer.

Vous pouvez améliorer votre exemple en ajoutant la chaîne de connexion et la valeur d'identification à la propriété de données de l'exception et en vous assurant qu'il est également connecté. De cette façon, vous vous donnez une chance de se battre pour reproduire l'erreur.


0 commentaires

6
votes

Regardez cet article de Krzysztof Cwalina, que j'ai trouvé très utile pour comprendre quand attraper ou ignorer des exceptions:

Comment concevoir des hiérarchies d'exception

Tous les principes qu'il décrit sur la conception des hiérarchies d'exceptions sont également applicables lorsque vous décidez quand attraper, lancer ou ignorer des exceptions. Il divise les exceptions en trois groupes:

  • erreurs d'utilisation , tels que DivideByzeroException , qui indiquent des erreurs dans le code; Vous ne devriez pas les gérer parce qu'ils peuvent être évités en changeant votre code.
  • Erreurs logiques , tels que Filenotfoundexception , que vous devez gérer car vous ne pouvez pas garantir qu'ils ne se produiront pas. (Même si vous vérifiez l'existence du fichier, cela pourrait toujours être supprimé dans cette fractionnement, seconde avant de le lire.)
  • échecs système , tels que OutofMemoryException , que vous ne pouvez pas éviter ou gérer.

0 commentaires

1
votes

Je conviens que, en général, vous ne devriez prendre des exceptions que vous attendez et comprenez comment gérer. Quelques cas où je ne fais souvent pas cela:

  1. Comme mentionné ci-dessus, si je capte une sorte d'informations utiles pour vous connecter, puis retirhow.

  2. Si j'effectue une opération asynchrone, telle que manipuler des messages en file d'attente ou des travaux dans un fil de travailleur, et je tiens à attirer l'exception pour retirer dans un contexte différent. J'utilise aussi souvent un piratage laids ici qui tente le CLR dans des informations sur les traces d'appoint ajoutées, de sorte qu'elle ne soit pas perdue lorsqu'elle repousse dans le nouveau contexte.

  3. Si je travaille avec une tâche ou une opération isolée et que je peux gérer l'exception en éteignant la tâche, sans fermer toute l'application. Je souhaite souvent ici qu'il y avait une exception de haut niveau pour des exceptions véritablement fatales (comme OutofMemoryException), comme je l'ignore. La bonne façon de gérer cela serait de gérer la tâche isolée dans sa propre appdomaine, mais je n'ai pas eu le temps disponible pour la mettre en œuvre sur un projet.


0 commentaires