Meilleures pratiques pour la gestion des exceptions en Java ou C# [fermé]

Je suis coincé à décider comment gérer les exceptions dans mon application.

Beaucoup si mes problèmes avec des exceptions proviennent de 1) l'accès aux données via un service distant ou 2) la désérialisation d'un objet JSON. Malheureusement, je ne peux pas garantir le succès de l'une ou l'autre de ces tâches (couper la connexion réseau, objet JSON mal formé qui est hors de mon contrôle).

Par conséquent, si je rencontre une exception, Je l'attrape simplement dans la fonction et renvoie FALSE à l'appelant. Ma logique est que tous les l'appelant se soucie vraiment de savoir si la tâche a réussi, pas pourquoi elle n'a pas réussi.

Voici un exemple de code (en JAVA) d'une méthode typique)

public boolean doSomething(Object p_somthingToDoOn)
{
    boolean result = false;

    try{
        // if dirty object then clean
        doactualStuffOnObject(p_jsonObject);

        //assume success (no exception thrown)
        result = true;
    }
    catch(Exception Ex)
    {
        //don't care about exceptions
        Ex.printStackTrace();
    }
    return result;
}

Je pense que cette approche est bonne, mais je suis vraiment curieux de savoir quelles sont les meilleures pratiques pour gérer les exceptions (devrais-je vraiment faire une exception dans une pile d'appels?).

En résumé des questions clés:

  1. est-il correct de simplement attraper des exceptions mais pas de les faire sauter ou formellement notification au système (via un journal ou une notification à l'utilisateur)?
  2. quelles sont les meilleures pratiques pour les exceptions qui n'entraînent pas tout ce qui nécessite un bloc try / catch?

Suivi / Édition

Merci pour tous les commentaires, trouvé d'excellentes sources sur la gestion des exceptions en ligne:

Il semble que la gestion des exceptions soit l'une de ces choses qui varient en fonction du contexte. Mais le plus important, il faut être cohérent dans la façon dont ils gèrent les exceptions au sein d'un système.

En outre, méfiez-vous de la pourriture du code via des tentatives/captures excessives ou ne donnant pas son respect à une exception (une exception est avertissement du système, quoi d'autre doit être averti?).

Aussi, c'est un joli commentaire de choix de m3rLinEz.

J'ai tendance à être d'accord avec Anders Hejlsberg et vous que le plus d'appelants seulement soins si l'opération est réussie ou non.

À partir de ce commentaire, il soulève quelques questions à réfléchir lors du traitement des exceptions:

  • à quoi sert cette exception?
  • Comment est-il logique de le gérer?
  • L'appelant se soucie-t-il vraiment de l'exception ou se soucie-t-il simplement si l'appel a réussi?
  • forcer un appelant à gérer une exception potentielle est-il gracieux?
  • êtes-vous respectueux des idoms de la langue?
    • avez-vous vraiment besoin de retourner un drapeau de succès comme booléen? Renvoyer booléen (ou un int) est plus un État d'esprit C Qu'un Java (en Java, vous géreriez simplement l'exception).
    • Suivez l'erreur constructions de gestion associées au langage:)!
116
demandé sur AtariPete 2009-01-03 21:41:52

15 réponses

Il me semble étrange que vous vouliez attraper des exceptions et les transformer en codes d'erreur. Pourquoi pensez-vous que l'appelant préférerait les codes d'erreur aux exceptions lorsque ce dernier est la valeur par défaut en Java et en C#?

Quant à vos questions:

  1. vous ne devriez attraper que les exceptions que vous pouvez réellement gérer. Juste attraper des exceptions n'est pas la bonne chose à faire dans la plupart des cas. Il y a quelques exceptions (par exemple, les exceptions de journalisation et de marshalling entre threads) mais même pour ces cas vous devriez généralement repasser les exceptions.
  2. vous ne devriez certainement pas avoir beaucoup d'instructions try/catch dans votre code. Encore une fois, l'idée est de ne capturer que les exceptions que vous pouvez gérer. Vous pouvez inclure un gestionnaire d'exception le plus haut pour transformer tout non géré exceptions en quelque chose d'utile pour l'utilisateur final mais sinon, vous ne devriez pas essayer d'attraper chaque exception dans chaque endroit possible.
60
répondu Brian Rasmussen 2012-05-02 05:06:46

Cela dépend de l'application et de la situation. Si vous construisez un composant de bibliothèque, vous devez créer des exceptions, bien qu'elles soient encapsulées pour être contextuelles avec votre composant. Par exemple, si vous construisez une base de données Xml et disons que vous utilisez le système de fichiers pour stocker vos données, et que vous utilisez les autorisations du système de fichiers pour sécuriser les données. Vous ne voudriez pas créer une exception FileIOAccessDenied car cela fuit votre implémentation. Au lieu de cela vous envelopper le exception et lancer une erreur AccessDenied. Cela est particulièrement vrai si vous distribuez le composant à des tiers.

Quant à savoir s'il est correct d'avaler des exceptions. Cela dépend de votre système. Si votre application peut gérer les cas d'échec et qu'il n'y a aucun avantage à notifier à l'utilisateur pourquoi il a échoué, alors allez-y, bien que je recommande fortement que votre journal l'échec. Je l'ai toujours trouvé frustrant d'être appelé pour aider à résoudre un problème et trouver qu'ils avalaient le exception (ou le remplacer et en lancer une nouvelle à la place sans définir l'exception interne).

, En général, j'utilise les règles suivantes:

  1. dans Mes composants et bibliothèques, Je n'attrape une exception que si j'ai l'intention de la gérer ou de faire quelque chose en fonction. Ou si je veux fournir des informations contextuelles supplémentaires dans une exception.
  2. j'utilise un try catch général au point d'entrée de l'application, ou au plus haut niveau possible. Si une exception arrive ici je le connecte et le laisse échouer. Idéalement, les exceptions ne devraient jamais arriver ici.

Je trouve que le code suivant est une odeur:

try
{
    //do something
}
catch(Exception)
{
   throw;
}

Un Code comme celui-ci ne sert à rien et ne doit pas être inclus.

25
répondu JoshBerke 2009-01-03 18:51:46

Je voudrais recommander une autre bonne source sur le sujet. C'est une interview avec les inventeurs de C# et Java, Anders Hejlsberg et James Gosling respectivement, sur le sujet de L'Exception vérifiée de Java.

Échec et Exceptions

Il y a aussi d'excellentes ressources au bas de la page.

J'ai tendance à être d'accord avec Anders Hejlsberg et vous que la plupart des appelants ne se soucient que si l'opération réussit ou non.

Bill Venners: Vous mentionner problèmes d'évolutivité et de gestion des versions relativement à la vérification des exceptions. Pourriez-vous préciser ce que vous entendez par ces deux questions?

Anders Hejlsberg : commençons par versioning, parce que les problèmes sont assez facile de voir, là. Disons que je créez une méthode foo qui la déclare lance les exceptions A, B et C. Dans version deux de foo, je veux ajouter un tas de fonctionnalités, et maintenant foo pourrait jeter exception D. Il est une rupture changer pour moi d'ajouter D les lancers clause de cette méthode, parce que appelant de cette méthode presque certainement pas gérer cela exception.

Ajout d'une nouvelle exception à un lancer clause dans une nouvelle version casse le client code. C'est comme ajouter une méthode à un interface. Après la publication d'un interface, il est pour tous pratique fins immuables, parce que tout la mise en œuvre de elle pourrait avoir méthodes que vous souhaitez ajouter dans le la prochaine version. Donc, vous avez à créer nouvelle interface à la place. Pareillement avec des exceptions, vous avez pour créer une toute nouvelle méthode appelée foo2 qui lève plus d'exceptions, ou vous devriez attraper l'exception D dans le nouveau foo, et transformer le D en un A, B ou C.

Bill Venners : mais ne cassez-vous pas leur code dans ce cas de toute façon, même dans une langue sans vérification des exceptions? Si la nouvelle version de foo va lancer une nouvelle exception les clients devraient penser à manutention, leur code n'est-il pas brisé juste par le fait qu'ils ne s'attendent pas que exception quand ils ont écrit le code?

Anders Hejlsberg: Non, parce que dans beaucoup des cas, les gens s'en foutent. Ils sont pas l'intention de traiter l'un de ces exception. Il y a un niveau inférieur gestionnaire d'exceptions autour de leur message boucle. Ce gestionnaire va faire apparaître une boîte de dialogue qui dit ce qui est allé mal et continuer. Programmeur protégez leur code en écrivant try enfin de partout, donc ils vont revenir correctement si une exception se produit, mais ils ne sont pas réellement intéressés par la manipulation des exceptions.

La clause throws, au moins le chemin il est implémenté en Java, n'est-ce pas nécessairement vous forcer à gérer le exceptions, mais si vous ne gérez pas entre eux, il vous oblige à reconnaître précisément quelles exceptions pourraient passer travers. Elle exige que l'on soit catch exceptions déclarées ou les mettre dans votre propre clause lancers. De travail autour de cette exigence, les gens font ridicule les choses. Par exemple, ils décorez chaque méthode avec, " jette Exception."Que juste complètement défaites la fonction, et vous venez de faire le programmeur écrire plus gobbledy crasse. Cela ne sert à personne.

EDIT: ajout de plus de détails sur le converstaion

9
répondu Gant 2009-01-03 21:55:54

Les exceptions vérifiées sont une question controversée en général, et en Java en particulier (plus tard, je vais essayer de trouver quelques exemples pour ceux qui sont en faveur et opposés à eux).

En tant que règles générales, la gestion des exceptions devrait être quelque chose autour de ces directives, sans ordre particulier:

  • par souci de maintenabilité, enregistrez toujours les exceptions afin que lorsque vous commencez à voir des bogues, le journal vous aide à vous diriger vers l'endroit où votre bogue a probablement commencé. Ne jamais quitter printStackTrace() ou les goûts de celui-ci, il y a des chances qu'un de vos utilisateurs obtienne éventuellement l'une de ces traces de pile, et ait exactement zéro connaissance de ce qu'il faut faire avec.
  • Attrapez les exceptions que vous pouvez gérer, et seulement celles-ci, et les gérer , ne les lancez pas simplement dans la pile.
  • attrapez toujours une classe d'exception spécifique, et généralement vous ne devriez jamais attraper le type Exception, Vous êtes très susceptible d'avaler des exceptions importantes autrement.
  • Ne jamais (jamais) attraper Errors!!, ce qui signifie: Ne jamais attraper Throwable s comme Error s sont des sous-classes de ce dernier. Error s sont des problèmes que vous ne pourrez probablement jamais gérer (par exemple OutOfMemory, ou d'autres problèmes JVM)

En ce qui concerne votre cas spécifique, assurez-vous que tout client appelant votre méthode recevra la valeur de retour appropriée. Si quelque chose échoue, une méthode de retour booléen peut renvoyer false, mais assurez-vous que les endroits que vous appelez cette méthode sont capables de gérer cela.

8
répondu Yuval Adam 2009-01-03 19:07:13

Vous ne devriez attraper que les exceptions que vous pouvez traiter. Par exemple, si vous avez affaire à la lecture sur un réseau et que la connexion expire et que vous obtenez une exception, vous pouvez réessayer. Cependant, si vous lisez sur un réseau et obtenez une exception IndexOutOfBounds, vous ne pouvez vraiment pas gérer cela parce que vous ne savez pas (eh bien, dans ce cas, vous ne le saurez pas) ce qui l'a causé. Si vous allez retourner false ou -1 ou null, assurez-vous que c'est pour les exceptions spécifiques. Je ne veux pas d'une bibliothèque que j'utilise renvoyer un false sur un réseau lu lorsque l'exception levée est que le tas est en panne de mémoire.

5
répondu Malfist 2009-01-03 18:57:45

Les Exceptions sont des erreurs qui ne font pas partie de l'exécution normale du programme. Selon ce que fait votre programme et ses utilisations (c'est-à-dire un traitement de texte par rapport à un moniteur cardiaque), vous voudrez faire des choses différentes lorsque vous rencontrez une exception. J'ai travaillé avec du code qui utilise des exceptions dans le cadre de l'exécution normale et c'est certainement une odeur de code.

Ex.

try
{
   sendMessage();

   if(message == success)
   {
       doStuff();
   }
   else if(message == failed)
   {
       throw;
   }
}
catch(Exception)
{
    logAndRecover();
}

CE code me fait vomir. IMO vous ne devriez pas récupérer des exceptions sauf si c'est un programme critique. Si votre lancer exceptions alors de mauvaises choses se produisent.

3
répondu Holograham 2009-01-03 19:14:17

Tout ce qui précède semble raisonnable, et souvent votre lieu de travail peut avoir une politique. À notre place, Nous avons défini les types D'Exception: SystemException (non coché) et ApplicationException (coché).

Nous avons convenu que les SystemExceptionsont peu susceptibles d'être récupérables et seront traités une fois au sommet. Pour fournir un contexte supplémentaire, nos SystemException s sont étendus pour indiquer où ils se sont produits, par exemple RepositoryException, ServiceEception, etc.

ApplicationExceptions pourrait avoir une signification commerciale comme InsufficientFundsException et devrait être géré par le client code.

Witohut un exemple concret, il est difficile de commenter votre implémentation, mais je n'utiliserais jamais de codes de retour, ils sont un problème de maintenance. Vous pouvez avaler une Exception, mais vous devez décider pourquoi, et toujours enregistrer l'événement et stacktrace. Enfin, comme votre méthode n'a pas d'autre traitement, elle est assez redondante (sauf pour l'encapsulation?), donc {[8] } pourrait renvoyer un booléen!

2
répondu 2009-01-03 19:16:13

Après avoir réfléchi et regardé votre code, il me semble que vous repensez simplement l'exception en tant que booléen. Vous pouvez simplement laisser la méthode passer cette exception (vous n'avez même pas besoin de l'attraper) et la traiter dans l'appelant, puisque c'est l'endroit où cela compte. Si l'exception entraîne l'appelant à réessayer cette fonction, l'appelant doit être celui qui attrape l'exception.

, Il peut parfois arriver que l'exception que vous rencontrez ne fera pas sens à l'appelant (c'est-à-dire une exception réseau), auquel cas vous devez l'envelopper dans une exception spécifique au domaine.

Si d'un autre côté, l'exception signale une erreur irrécupérable dans votre programme (c'est-à-dire que le résultat final de cette exception sera la fin du programme), j'aime personnellement le rendre explicite en l'attrapant et en lançant une exception d'exécution.

1
répondu wds 2009-01-03 18:56:38

Si vous allez utiliser le modèle de code dans votre exemple, appelez-le TryDoSomething, et n'attrapez que des exceptions spécifiques.

Aussi pensez à utiliser un filtre D'Exception lors de la journalisation des exceptions à des fins de diagnostic. VB prend en charge la langue pour les filtres D'Exception. Le lien vers le blog de Greggm a une implémentation qui peut être utilisée à partir de C#. Les filtres d'Exception ont de meilleures propriétés de débogage sur catch et rethrow. Plus précisément, vous pouvez enregistrer le problème dans le filtrer et laisser l'exception continuer à se propager. Cette méthode permet à un débogueur JIT (juste à temps) d'avoir la pile d'origine complète. Un rethrow coupe la pile au point où elle a été rethrown.

Les cas où TryXXXX a du sens sont lorsque vous enveloppez une fonction tierce qui lance dans des CAs qui ne sont pas vraiment exceptionnels, ou qui sont simples et difficiles à tester sans appeler la fonction. Un exemple serait quelque chose comme:

// throws NumberNotHexidecimalException
int ParseHexidecimal(string numberToParse); 

bool TryParseHexidecimal(string numberToParse, out int parsedInt)
{
     try
     {
         parsedInt = ParseHexidecimal(numberToParse);
         return true;
     }
     catch(NumberNotHexidecimalException ex)
     {
         parsedInt = 0;
         return false;
     }
     catch(Exception ex)
     {
         // Implement the error policy for unexpected exceptions:
         // log a callstack, assert if a debugger is attached etc.
         LogRetailAssert(ex);
         // rethrow the exception
         // The downside is that a JIT debugger will have the next
         // line as the place that threw the exception, rather than
         // the original location further down the stack.
         throw;
         // A better practice is to use an exception filter here.
         // see the link to Exception Filter Inject above
         // http://code.msdn.microsoft.com/ExceptionFilterInjct
     }
}

Si vous utilisez un modèle comme TryXXX ou non est plus d'une question de style. La question d'attraper toutes les exceptions et de les avaler n'est pas un problème de style. Assurez-vous que les exceptions inattendues sont autorisées à se propager!

1
répondu Steve Steiner 2009-01-03 21:40:59

Je suggère de prendre vos repères de la bibliothèque standard pour la langue que vous utilisez. Je ne peux pas parler pour C#, mais regardons Java.

Par exemple java.lang.refléter.Le tableau a une méthode statique set:

static void set(Object array, int index, Object value);

La voie C serait

static int set(Object array, int index, Object value);

... avec la valeur de retour étant un indicateur de succès. Mais vous n'êtes plus dans C world.

Une fois que vous adoptez des exceptions, vous devriez constater que cela rend votre code plus simple et plus clair, en éloignant votre code de gestion des erreurs à partir de votre base de la logique. Visez à avoir beaucoup d'instructions dans un seul bloc try.

Comme d'autres l'ont noté, vous devez être aussi précis que possible dans le type d'exception-vous attraper.

1
répondu slim 2009-01-04 00:57:56

Si vous allez attraper une Exception et retourner false, cela devrait être une exception très spécifique. Vous ne faites pas ça, vous les attrapez tous et vous retournez faux. Si je reçois une MyCarIsOnFireException, je veux le savoir tout de suite! Le reste des Exceptions que je pourrais ne pas me soucier. Donc, vous devriez avoir une pile de gestionnaires D'exceptions qui disent "whoa whoa quelque chose ne va pas ici" pour certaines exceptions (rethrow, ou catch et rethrow une nouvelle exception qui explique mieux ce qui s'est passé) et il suffit de retourner false pour les autres.

S'il s'agit d'un produit que vous lancerez, vous devriez enregistrer ces exceptions quelque part, cela vous aidera à régler les choses à l'avenir.

Edit: quant à la question de tout emballer dans un try/catch, je pense que la réponse est oui. Les Exceptions devraient être si rares dans votre code que le code du bloc catch s'exécute si rarement qu'il n'atteint pas du tout les performances. Une exception devrait être un état où votre machine d'état s'est cassée et ne le fait pas savoir quoi faire. Au moins renvoyer une exception qui explique ce qui se passait au temps et a l'exception interceptée à l'intérieur. "Exception dans la méthode doSomeStuff()" n'est pas très utile pour quiconque doit comprendre pourquoi il s'est cassé pendant que vous êtes en vacances (ou à un nouvel emploi).

0
répondu jcollum 2009-01-03 18:52:29

Ma stratégie:

Si la fonction d'origine est retourné void je l'ai changer pour revenir bool. Si exception/erreur s'est produite retour false, si tout s'est bien de retour true.

Si la fonction doit renvoyer quelque chose, lorsque l'exception/erreur s'est produite, renvoie null, sinon l'élément retournable.

Au Lieu de bool un string peut être retourné contenant la description de l'erreur.

Dans tous les cas avant de renvoyer quoi que ce soit, enregistrez l'erreur.

0
répondu Germstorm 2009-01-03 19:37:53

Quelques excellentes réponses ici. Je voudrais ajouter, que si vous vous retrouvez avec quelque chose comme vous avez posté, au moins imprimer plus que la trace de la pile. Dites ce que vous faisiez à l'époque, et Ex.getMessage (), pour donner au développeur une chance de combat.

0
répondu dj_segfault 2009-01-03 21:34:51

Les blocs Try/catch forment un deuxième ensemble de logique intégré au premier ensemble (principal), en tant que tels, ils sont un excellent moyen de sortir du code spaghetti illisible et difficile à déboguer.

Pourtant, utilisé raisonnablement, ils font des merveilles en lisibilité, mais vous devriez simplement suivre deux règles simples:

  • Utilisez-les (avec parcimonie) au niveau bas pour attraper les problèmes de gestion de la bibliothèque et les rediffuser dans le flux logique principal. La plupart de la gestion des erreurs que nous voulons, devrait provenir du code elle-même, dans le cadre des données elles-mêmes. Pourquoi faire des conditions spéciales, si les données retournées ne sont pas spéciales?

  • Utilisez un grand gestionnaire au niveau supérieur pour gérer tout ou partie des conditions étranges survenant dans le code qui ne sont pas prises à un niveau bas. Faites quelque chose d'utile avec les erreurs (journaux, redémarrages, récupérations, etc.).

En dehors de ces deux types de gestion des erreurs, tout le reste du code au milieu doit être libre et exempt de code try / catch et d'erreur objet. De cette façon, il fonctionne simplement et comme prévu, peu importe où vous l'utilisez, ou ce que vous faites avec elle.

Paul.

0
répondu Paul W Homer 2009-01-04 00:14:15

Je suis peut-être un peu en retard avec la réponse, mais la gestion des erreurs est quelque chose que nous pouvons toujours changer et évoluer avec le temps. Si vous voulez lire quelque chose de plus sur ce sujet, j'ai écrit un post dans mon nouveau blog à ce sujet. http://taoofdevelopment.wordpress.com

Codage heureux.

0
répondu GRGodoi 2011-02-01 21:42:34