6
votes

Erreurs de mélange et exceptions en C #

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:

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?

Je pourrais jeter une nouvelle exception comme ci-dessous, mais je suis inquiet que je vais gâcher up the stacktrace qui est mauvais: xxx

merci, marque


5 commentaires

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 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.


7 Réponses :


5
votes

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.


6 commentaires

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 - 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 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 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.



7
votes

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à.

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 Je préférerais absolument trygetvalue 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.

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

  • ne peut pas être prédit: fichier non trouvé
  • violation du contrat: transmettre un index négatif dans un indexeur

0 commentaires

2
votes

'callee' effectue quelques choses différentes, chacune pouvant lancer le même type d'exception.

Je pense que c'est le vrai coupable: callee 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.


0 commentaires

3
votes

Dans les scénarios comme celui-ci, j'injecte habituellement un enregistreur et enregistre l'erreur spécifique et retire l'exception originale: xxx

the lancer; gardera la trace d'originale de la pile en vie (par opposition à lancer ex; qui créera une nouvelle stacktrace).

EDIT

le code fait qu'il pourrait avoir du sens pour casser réellement callee dans plusieurs appels ( Connexion () , sendfile () , etc.), donc L'appelant 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)


4 commentaires

+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.



0
votes

Si vous véritablement 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.

vous pouvez soit

  • 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, Membership.createUser 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.

  • 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.


0 commentaires

2
votes

Pourquoi l'appelant est-il en soin de quoi callee faisait? Cela prendra-t-il une action différente si le problème s'est produit lors de l'envoi du fichier? XXX

En règle générale, les exceptions doivent être exceptionnelles . 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 supposé être à cause d'un bogue dans votre code *. Le but principal d'une exception est donc:

  1. Pour arrêter le flux de la logique avant que le bogue ne provoque plus de dégâts, et
  2. Pour fournir des informations sur l'état du système lorsque le bogue s'est produit, de sorte que vous pouvez identifier la source du bogue.

    Dans cet esprit, vous ne devez généralement pas attraper des exceptions si:

    1. Vous prévoyez d'envelopper l'exception avec des données supplémentaires (comme dans votre exemple) et de lancer une nouvelle exception, ou
    2. vous savez qu'il n'y aura plus d'effets néfastes de continuer avec votre flux logique: vous reconnaissez que la chose que vous avez essayée a échoué et que vous n'êtes pas échoué, vous êtes en paix avec cette. Dans ces cas, vous devez toujours profiter de l'occasion pour enregistrer l'erreur.

      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 Signature de la méthode de sorte que son résultat renvoyé fournit tous les détails que l'appelant doit savoir ce qui s'est mal passé.

      * 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.


0 commentaires

1
votes

L'exception classe fournit un dictionnaire pour ajouter des données supplémentaires à une exception accessible via la propriété exception.data .

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: xxx

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.


0 commentaires