J'ai regardé, mais je n'ai pas trouvé de réponse définitive pour certaines de mes questions d'exception, notamment en ce qui concerne les meilleures pratiques C #.
Je serai peut-être toujours confus sur la meilleure façon d'utiliser des exceptions, j'ai couru sur cet article Ce qui dit essentiellement 'utilisez toujours des exceptions, n'utilisez jamais de codes d'erreur ou de propriétés' http: //www.eggheadcafe. com / articles / 20060612.asp . Je vais certainement acheter ça, mais voici mon dilemme: p>
J'ai une fonction "appelant" qui appelle "callee". 'Callee' effectue quelques choses différentes, chacune pouvant lancer le même type d'exception. Comment puis-je relu des informations significatives à "appelant" sur ce que "callee" faisait au moment de l'exception? P>
Je pourrais jeter une nouvelle exception comme ci-dessous, mais je suis inquiet que je vais gâcher up the stacktrace qui est mauvais: p> merci, marque p> p>
7 Réponses :
L'appelant n'a pas besoin de savoir ce que la Callee faisait au moment de l'exception. Il ne devrait pas savoir quoi que ce soit sur la mise en œuvre de la callee. P>
Comme pour toutes les déclarations de couverture, celle-ci est également trompeuse (oh l'ironie). Si c'était vrai, il n'y aurait pas de bazillion de types d'exception différents dans .NET.
Il n'y a pas de bazillion de types d'exception. De plus, ces types n'indiquent pas "ce que faisait la callee"; Ils indiquent le résultat de la callee le faisant, ce qui est assez différent.
Je vous oublie de cliquer sur ici (et considère que Ce ne sont que les enfants immédiats de system.Exception code> - le plaisir continue par exemple ici ). Quant à la distinction d'action vs résultat, il s'agit d'un hareng rouge; personne ne jette à l'exception des exceptions à moins que est i> un résultat pour signaler (un indésirable).
Cela ne m'aident pas à résoudre mon problème, mais c'est une déclaration valable et quelque chose que je n'en tenais pas compte. Utile.
@Jon: L'appelant devrait se soucier de l'état i> de choses lorsque l'exception est lancée. En dehors du débogage ou de la dépannage des contextes, cela ne devrait généralement se soucier que de ce que l'appelant "faire" uniquement lorsque ces informations permettent d'inférences utiles sur l'état du système. Malheureusement, la hiérarchie exception existante n'est pas très efficace pour transmettre ce qui est vraiment important.
En fait, non, je pense que l'appelant ne devrait se soucier que de son propre État. Il ne devrait ni savoir ni ne se soucier de l'état des méthodes appels. Sinon, les méthodes deviennent trop étroitement couplées. Selon vous, que devrait être transmis? Je trouve que cela est rare qu'un appelant doit différencier les exceptions projetées par une méthode qu'elle appelle.
Il n'y a pas de meilleure pratique pour couvrir tous les scénarios de .NET. Quiconque dit "toujours utiliser des exceptions" ou "toujours utiliser les codes d'erreur" est tout simplement faux. Les programmes écrits dans .NET sont à large et complexe pour généraliser dans une règle dure et rapide comme celle-là. P>
Il y a de nombreux cas où des exceptions ne sont tout simplement pas appropriées. Par exemple, si j'ai une boucle très étanche à la recherche de valeurs dans un dictionnaire Le meilleur moyen d'aborder cette décision est au cas par cas. En général, je n'utilise que des exceptions si la situation est vraiment exceptionnelle. Un élément essentiel qui n'a pas pu être prédit par l'appelant ou les résultats de l'appelant violant explicitement un contrat. Exemples P>
trygetvalue code> sur une indexer de lancer. Pourtant, dans d'autres cas, je préférerais le lancer si les données fournies à moi ont été contractées pour être déjà sur la carte. p>
'callee' effectue quelques choses différentes, chacune pouvant lancer le même type d'exception. P> blockQuote>
Je pense que c'est le vrai coupable:
callee code> devrait jeter un type d'exception différent basé sur un type d'échec, ou les exceptions du même type doivent porter suffisamment d'informations supplémentaires pour que L'appelant montre comment gérer cette exception particulière. Bien sûr, si l'appelant n'est pas destiné à gérer l'exception du tout, cela ne devrait pas être dans l'affaire de l'attraper en premier lieu. P>
Dans les scénarios comme celui-ci, j'injecte habituellement un enregistreur et enregistre l'erreur spécifique et retire l'exception originale: the le code fait qu'il pourrait avoir du sens pour casser réellement lancer; code> gardera la trace d'originale de la pile en vie (par opposition à
lancer ex; code> qui créera une nouvelle stacktrace). p>
callee code> dans plusieurs appels (
Connexion () code>,
sendfile () code>, etc.), donc
L'appelant code> effectue les étapes individuelles au lieu d'appeler une grande méthode qui fait tout. Ensuite, l'appelant saura où il a échoué et peut y agir (peut-être demander à l'utilisateur de différentes informations de connexion) p> p>
+1 C'est une bonne idée si vous devez organiser une exception et j'ai fait beaucoup de cela dans le passé lorsqu'il s'agit de déposer des exceptions IO.
C'est en fait ce que je faisais. Je pourrais descendre la route de la poignée de callee sans exception et que l'appelant essaie / attrape-les et obtenez des informations supplémentaires à partir du journal si nécessaire.
@Mstodd: Cela pourrait être logique de rompre Callee s'il est important que l'appelant sache à quelle étape le problème s'est produit.
@Chriswue pas dans ce cas. En outre, si la callee fonctionne sur des objets dans une liste et que l'un de ces objets jette une exception, qui ne peut pas être fromblic si la Callee veut savoir quel objet a lancé l'exception.
Si vous véritablement em> avez un scénario où un appelant doit gérer les exceptions différemment, la stratégie typique consiste à "attraper et à envelopper" l'exception sous-jacente dans la callee. P>
vous pouvez soit p>
envelopper toutes les exceptions sous-jacentes dans un type d'exception personnalisée, avec un code d'état indiquant quelle exception sous-jacente a été lancée. Ceci est courant lors de l'utilisation du modèle de conception de modèle de fournisseur. Par exemple, ou enveloppez chaque type d'exception sous-jacente dans une exception personnalisée séparée. Cela pourrait être approprié si vous vous attendez à ce que certains appelants puissent vouloir gérer l'une des exceptions (par exemple, l'échec des fichiers d'envoi) mais non des autres (par exemple, l'échec de la connexion). En utilisant des types d'exception différents, l'appelant n'a besoin que d'attraper ceux qui intéressent. P> li>
ul>
Membership.createUser Code> peut lancer un MembershipCreateuserException qui a un code de statut pour distinguer les raisons communes de l'échec. Les responsables de la mise en œuvre des fournisseurs devraient suivre ce modèle de manière à ce que la gestion des excepteurs de la consommation soit indépendante de la mise en œuvre du fournisseur. P> LI>
Pourquoi l'appelant En règle générale, les exceptions doivent être exceptionnelles em>. Votre flux logique typique ne doit pas nécessiter qu'ils soient jetés et attrapés. Quand ils sont lancés, il est généralement em> supposé être à cause d'un bogue dans votre code *. Le but principal d'une exception est donc: p> Dans cet esprit, vous ne devez généralement pas attraper des exceptions si: P> S'il existe un cas assez commun pour l'échec (par exemple, l'utilisateur fournissait des informations d'identification de login incorrectes), cela ne doit pas être traité comme une exception. Plutôt, vous devez structurer votre «code> callee code> Signature de la méthode de sorte que son résultat renvoyé fournit tous les détails que l'appelant * L'exception notable est lorsque quelque chose de vraiment imprévisible se passe, comme si quelqu'un a débranché le câble réseau de l'ordinateur au milieu de la transaction. sup> p> p> p> P> P> code> est-il en soin de quoi
callee code> faisait? Cela prendra-t-il une action différente si le problème s'est produit lors de l'envoi du fichier?
code> doit savoir ce qui s'est mal passé. P>
L'exception Si tout ce que vous voulez faire On raconte des bits d'informations supplémentaires à l'appelant, alors il n'y a aucune raison d'envelopper l'exception originale et d'en lancer un nouveau. Au lieu de cela, vous pouvez simplement faire: p> De cette façon, vous pourrez préserver la structure d'origine pendant que vous pouvez toujours présenter des informations supplémentaires à l'utilisateur. P> P > code> classe fournit un dictionnaire pour ajouter des données supplémentaires à une exception accessible via la propriété
exception.data code>.
Exceptions Bubble Up La pile d'appel automatiquement. Vous n'avez pas à repousser.
@Codygray La raison de Retirhowing est d'ajouter des informations afin que l'appelant puisse la différencier entre eux. Si je me distingue l'essai / Catch de la callee, l'appelant ne saura pas quelle opération de multiples opérations a provoqué l'exception.
Je ne comprends pas pourquoi la Callee ne voudrait pas simplement lancer une exception contenant des informations sur ce qu'elle faisait. L'appelant ne devrait pas savoir ce que fait la callee de toute façon!
Pouvez-vous montrer un exemple d'où l'appelant besoin i> de distinguer des exceptions? L'appelant fera-t-il différentes choses en fonction de l'exception?
Ne pas contrôler le flux d'exécution, mais veut simplement relâcher plus d'informations à l'interface utilisateur que ne peut être glanée de l'exception lancée.