web-dev-qa-db-fra.com

Quand lancer une exception?

J'ai des exceptions créées pour chaque condition à laquelle mon application ne s'attend pas. UserNameNotValidException, PasswordNotCorrectException etc.

Cependant, on m'a dit que je ne devrais pas créer d'exceptions pour ces conditions. Dans mon UML, ces exceptions sont des exceptions au flux principal, alors pourquoi ne devrait-il pas s'agir d'une exception?

Des conseils ou des meilleures pratiques pour créer des exceptions?

405
Kwan Cheng

Ma ligne de conduite personnelle est la suivante: une exception est levée lorsqu'une hypothèse fondamentale du bloc de code actuel est fausse.

Exemple 1: disons que j'ai une fonction censée examiner une classe arbitraire et renvoyer true si cette classe hérite de List <>. Cette fonction pose la question "Cet objet est-il un descendant de List?" Cette fonction ne doit jamais émettre d'exception car il n'y a pas de zones grises dans son fonctionnement. Chaque classe hérite ou non de List <>. La réponse est donc toujours "oui" ou "non".

Exemple 2: disons que j’ai une autre fonction qui examine une liste <> et renvoie vrai si sa longueur est supérieure à 50 et faux si la longueur est inférieure. Cette fonction pose la question "Cette liste contient-elle plus de 50 éléments?" Mais cette question suppose une hypothèse - elle suppose que l'objet qui lui est donné est une liste. Si je lui donne un NULL, alors cette hypothèse est fausse. Dans ce cas, si la fonction retourne soit vrai ou faux, alors il enfreint ses propres règles. La fonction ne peut pas retourner n'importe quoi et prétendent avoir répondu correctement à la question. Donc, il ne revient pas - il lève une exception.

Ceci est comparable au "question chargée" erreur logique. Chaque fonction pose une question. Si l'entrée qui est donnée fait de cette question une erreur, jetez une exception. Cette ligne est plus difficile à tracer avec des fonctions qui retournent void, mais le résultat final est le suivant: si les suppositions de la fonction sur ses entrées sont violées, elle devrait lever une exception au lieu de revenir normalement.

L’autre côté de cette équation est le suivant: si vous trouvez que vos fonctions lèvent fréquemment des exceptions, vous devrez probablement affiner leurs hypothèses.

590
The Digital Gabeg

Parce que ce sont des choses qui vont se passer normalement. Les exceptions ne sont pas des mécanismes de flux de contrôle. Les utilisateurs obtiennent souvent des mots de passe incorrects, ce n'est pas un cas exceptionnel. Les exceptions devraient être une chose vraiment rare, des situations de type UserHasDiedAtKeyboard.

278
blowdart

Mon petit guide est fortement influencé par le grand livre "Code complete":

  • Utilisez des exceptions pour notifier des choses à ne pas négliger.
  • N'utilisez pas d'exceptions si l'erreur peut être gérée localement
  • Assurez-vous que les exceptions sont au même niveau d'abstraction que le reste de votre routine.
  • Les exceptions doivent être réservées à ce qui est vraiment exceptionnel.
61
Commander Keen

Ce n'est PAS une exception si le nom d'utilisateur n'est pas valide ou si le mot de passe est incorrect. Ce sont des choses auxquelles vous devriez vous attendre dans le déroulement normal des opérations. Les exceptions sont des choses qui ne font pas partie du fonctionnement normal du programme et qui sont plutôt rares.

EDIT: Je n'aime pas utiliser les exceptions car vous ne pouvez pas savoir si une méthode lève une exception simplement en regardant l'appel. C’est pourquoi les exceptions ne devraient être utilisées que si vous ne pouvez pas gérer la situation de manière décente (pensez "à court de mémoire" ou "l’ordinateur est en feu").

34
EricSchaefer

Une règle empirique consiste à utiliser des exceptions dans le cas de quelque chose que vous ne pouvez pas prédire normalement. Exemples: connectivité à la base de données, fichier manquant sur le disque, etc. Pour les scénarios que vous pouvez prédire, par exemple les utilisateurs qui tentent de se connecter avec un mot de passe incorrect, vous devez utiliser des fonctions qui renvoient des booléens et savent comment gérer la situation avec élégance. Vous ne voulez pas mettre fin brusquement à l'exécution en lançant une exception simplement parce que quelqu'un a mal saisi son mot de passe.

25
japollock

D'autres proposent que les exceptions ne soient pas utilisées car il faut s'attendre à une mauvaise connexion dans un flux normal si les types de l'utilisateur sont erronés. Je ne suis pas d'accord et je ne comprends pas le raisonnement. Comparez-le avec l'ouverture d'un fichier. Si le fichier n'existe pas ou n'est pas disponible pour une raison quelconque, une exception sera levée par le framework. En utilisant la logique ci-dessus, c'était une erreur de Microsoft. Ils auraient dû retourner un code d'erreur. Idem pour l'analyse syntaxique, les requêtes en ligne, etc., etc.

Je ne considère pas qu'une mauvaise connexion fasse partie d'un flux normal, c'est exceptionnel. Normalement, l'utilisateur tape le mot de passe correct et le fichier existe. Les cas exceptionnels sont exceptionnels et il est parfaitement correct d’utiliser des exceptions pour ceux-ci. Compliquer votre code en propageant les valeurs de retour sur n niveaux en pile est un gaspillage d’énergie qui entraînera un code désordonné. Faites la chose la plus simple qui puisse fonctionner. N'optimisez pas prématurément en utilisant des codes d'erreur, des choses exceptionnelles, par définition, arrivent rarement, et les exceptions ne coûtent rien à moins que vous ne les jetiez.

23
Bjorn Reppen

Les exceptions ont un effet quelque peu coûteux. Par exemple, si vous avez un utilisateur qui fournit un mot de passe invalide, il est généralement préférable de renvoyer un indicateur d'échec ou un autre indicateur indiquant qu'il est invalide.

Cela est dû à la façon dont les exceptions sont gérées, à une entrée vraiment mauvaise et aux éléments d'arrêt critiques uniques qui doivent être des exceptions, mais pas aux informations de connexion en échec.

16
Mitchel Sellers

Je pense que vous ne devriez lancer une exception que lorsque vous ne pouvez rien faire pour sortir de votre état actuel. Par exemple, si vous allouez de la mémoire et qu'il n'y en a pas. Dans les cas que vous mentionnez, vous pouvez clairement récupérer de ces états et renvoyer un code d'erreur à votre appelant en conséquence.


Vous verrez de nombreux conseils, y compris dans les réponses à cette question, selon lesquels vous ne devriez lever les exceptions que dans des circonstances "exceptionnelles". Cela semble superficiellement raisonnable, mais constitue un conseil erroné, car il remplace une question ("quand devrais-je lever une exception") par une autre question subjective ("ce qui est exceptionnel"). Suivez plutôt les conseils de Herb Sutter (pour C++, disponible dans la article du Dr Dobbs ) Quand et comment utiliser les exceptions , et aussi dans son livre avec Andrei Alexandrescu, Normes de codage C++ ): lève une exception si, et seulement si

  • une condition préalable n’est pas remplie (ce qui rend généralement l’un des suivants impossible) ou
  • l'alternative ne satisferait pas à une post-condition ou
  • l'alternative ne parviendrait pas à maintenir un invariant.

Pourquoi est-ce mieux? Ne remplace-t-il pas la question par plusieurs questions sur les préconditions, les post-conditions et les invariants? C'est mieux pour plusieurs raisons liées.

  • Les préconditions, les post-conditions et les invariants sont la conception des caractéristiques de notre programme (son API interne), alors que la décision de throw est un détail de la mise en oeuvre. Cela nous oblige à garder à l'esprit que nous devons considérer la conception et sa mise en œuvre séparément, et notre travail de mise en œuvre d'une méthode consiste à produire quelque chose qui réponde aux contraintes de conception.
  • Cela nous oblige à penser en termes de conditions préalables, post-conditions et invariants, qui sont les hypothèses seulement que les appelants de notre méthode doivent faire et sont exprimées avec précision, permettant un couplage lâche entre les composants de notre programme.
  • Ce couplage lâche nous permet ensuite de refactoriser la mise en oeuvre, si nécessaire.
  • Les post-conditions et les invariants sont testables; il en résulte un code qui peut facilement être testé par unité, car les post-conditions sont des prédicats que notre code de test unitaire peut vérifier (assert).
  • Penser en termes de post-conditions produit naturellement un design qui a le succès en tant que post-condition , qui est le style naturel pour utiliser des exceptions. Le chemin d’exécution normal ("heureux") de votre programme est présenté linéairement, avec tout le code de traitement des erreurs déplacé vers les clauses catch.
14
Jon

Je dirais qu'il n'y a pas de règles strictes sur le moment d'utiliser des exceptions. Cependant, il y a de bonnes raisons de les utiliser ou de ne pas les utiliser:

Raisons d'utiliser des exceptions:

  • Le flux de code pour le cas commun est plus clair
  • Peut renvoyer des informations d'erreur complexes sous forme d'objet (bien que cela puisse également être obtenu à l'aide du paramètre d'erreur "out" passé par référence)
  • Les langages fournissent généralement une certaine facilité pour gérer le nettoyage ordonné en cas d’exception (try/finally en Java, utilisation en C #, RAII en C++).
  • Si aucune exception n'est levée, l'exécution peut parfois être plus rapide que la vérification des codes de retour
  • En Java, les exceptions vérifiées doivent être déclarées ou interceptées (bien que cela puisse être une raison contre)

Raisons pour ne pas utiliser d'exceptions:

  • Parfois, c'est excessif si la gestion des erreurs est simple
  • Si les exceptions ne sont pas documentées ou déclarées, elles peuvent être non capturées par le code appelant, ce qui peut être pire que si le code appelant ignorait simplement un code de retour (sortie de l'application vs échec silencieux - ce qui est pire peut dépendre du scénario)
  • En C++, le code qui utilise des exceptions doit être protégé contre les exceptions (même si vous ne les lancez pas, mais appelez une fonction de projection indirectement)
  • En C++, il est difficile de dire quand une fonction peut lancer, vous devez donc être paranoïaque à propos de la sécurité des exceptions si vous les utilisez
  • Lancer et attraper des exceptions est généralement nettement plus coûteux que de vérifier un drapeau de retour

En général, je serais plus enclin à utiliser des exceptions dans Java qu'en C++ ou C #, car j'estime qu'une exception, déclarée ou non, fait fondamentalement partie de l'interface formelle d'une fonction, puisque changer votre garantie d'exception peut rompre le code d'appel. Le principal avantage de leur utilisation dans Java IMO est que vous savez que votre appelant DOIT gérer l'exception, ce qui améliore les chances de comportement correct.

Pour cette raison, quelle que soit la langue, toutes les exceptions contenues dans une couche de code ou d'API sont toujours extraites d'une classe commune, de sorte que le code d'appel peut toujours garantir que toutes les exceptions sont interceptées. De même, je considérerais qu'il est mauvais de lancer des classes d'exception spécifiques à l'implémentation lors de l'écriture d'une API ou d'une bibliothèque (c'est-à-dire d'encapsuler les exceptions des couches inférieures afin que l'exception que votre appelant reçoive soit compréhensible dans le contexte de votre interface).

Notez que Java fait la distinction entre les exceptions générales et les exceptions d'exécution en ce sens que celles-ci n'ont pas besoin d'être déclarées. Je n’utiliserais les classes d’exception Runtime que si vous savez que l’erreur résulte d’un bogue dans le programme.

10
Robert

Les classes d'exception sont comme des classes "normales". Vous créez une nouvelle classe quand il "s'agit" d'un type d'objet différent, avec des champs et des opérations différents.

En règle générale, vous devez essayer d’équilibrer le nombre d’exceptions et la granularité des exceptions. Si votre méthode lève plus de 4 à 5 exceptions différentes, vous pouvez probablement fusionner certaines d’entre elles en exceptions plus "générales" (par exemple dans votre cas "AuthenticationFailedException"), et en utilisant le message d’exception pour détailler ce qui ne va pas. À moins que votre code ne les traite différemment, vous n'avez pas besoin de créer beaucoup de classes d'exceptions. Et si c'est le cas, vous devriez simplement renvoyer une enum avec l'erreur qui s'est produite. C'est un peu plus propre de cette façon.

5
Shachar

Si le code qui tourne dans une boucle est susceptible de provoquer une exception encore et encore, alors lancer des exceptions n'est pas une bonne chose, car elles sont assez lentes pour les grands N, mais il n'y a rien de mal à lancer des exceptions personnalisées si les performances ne sont pas satisfaisantes. un problème. Assurez-vous simplement que vous avez une exception de base héritée, appelée BaseException ou quelque chose du genre. BaseException hérite de System.Exception, mais toutes vos exceptions héritent de BaseException. Vous pouvez même avoir une arborescence de types Exception pour regrouper des types similaires, mais cela peut être excessif ou non.

Donc, la réponse courte est que si cela n'entraîne pas de pénalité de performance significative (ce qui ne devrait pas être le cas sauf si vous jetez beaucoup d'exceptions), continuez.

5
Charles Graham

Je suis d'accord avec japollock bien là-haut - jetez un point de vue lorsque vous n'êtes pas certain du résultat d'une opération. Appels aux API, accès aux systèmes de fichiers, appels à la base de données, etc. Chaque fois que vous dépassez les "limites" de vos langages de programmation.

J'aimerais ajouter, n'hésitez pas à lancer une exception standard. À moins que vous ne fassiez quelque chose de "différent" (ignorer, envoyer un courrier électronique, vous connecter, montrer cette chose Twitter, etc), alors ne vous embêtez pas avec les exceptions personnalisées.

3
dclaysmith

la règle de base pour lancer des exceptions est assez simple. vous le faites lorsque votre code est entré dans un état INVALID INVISIBLE. si les données sont compromises ou si vous ne pouvez pas récupérer le traitement qui a eu lieu jusqu'au point, vous devez le terminer. En effet, que pouvez-vous faire d'autre? votre logique de traitement finira par échouer ailleurs. si vous pouvez récupérer d'une manière ou d'une autre, faites-le et ne jetez pas d'exception.

dans votre cas particulier, si vous étiez obligé de faire quelque chose de stupide, comme accepter un retrait d'argent et vérifier ensuite utilisateur/mot de passe, vous devez mettre fin au processus en lançant une exception pour signaler qu'un problème grave s'est produit et éviter des dommages supplémentaires.

3
goran

Je dirais que généralement tout fondamentalisme mène à l'enfer.

Vous ne voudriez certainement pas vous retrouver avec un flux dirigé par des exceptions, mais éviter les exceptions est également une mauvaise idée. Vous devez trouver un équilibre entre les deux approches. Ce que je ne voudrais pas faire, c'est créer un type d'exception pour chaque situation exceptionnelle. Ce n'est pas productif.

Ce que je préfère généralement, c’est de créer deux types de base d’exceptions qui sont utilisés dans l’ensemble du système: LogicalException et TechnicalException. Ceux-ci peuvent être encore distingués par des sous-types si nécessaire, mais ce n'est généralement pas nécessaire.

L'exception technique indique une exception vraiment inattendue, telle qu'une panne de serveur de base de données, une connexion au service Web renvoyant une exception IOException, etc.

D'autre part, les exceptions logiques sont utilisées pour propager la situation erronée moins grave aux couches supérieures (généralement un résultat de validation).

Veuillez noter que même l’exception logique n’est pas destinée à être utilisée régulièrement pour contrôler le flux du programme, mais plutôt pour mettre en évidence la situation dans laquelle le flux devrait réellement se terminer. Lorsqu'ils sont utilisés en Java, les deux types d'exception sont RuntimeException sous-classes et la gestion des erreurs est fortement orientée aspect.

Ainsi, dans l'exemple de connexion, il pourrait être judicieux de créer quelque chose comme AuthenticationException et de distinguer les situations concrètes par des valeurs enum telles que sernameNotExisting, PasswordMismatch etc. Dans ce cas, vous ne vous retrouverez pas dans avoir une énorme hiérarchie d'exceptions et peut garder les blocs catch au niveau maintenable. Vous pouvez également utiliser facilement un mécanisme de gestion des exceptions génériques, car vous avez classé les exceptions et vous savez très bien quoi propager à l'utilisateur et comment.

Notre utilisation typique consiste à lever l'exception LogicalException lors de l'appel du service Web lorsque la saisie de l'utilisateur n'était pas valide. L'exception est regroupée dans le détail SOAPFault, puis restituée à l'exception sur le client, ce qui a pour effet d'afficher l'erreur de validation sur un certain champ de saisie de page Web, étant donné que l'exception est correctement mappée sur ce champ.

Ce n'est certainement pas la seule situation: vous n'avez pas besoin de faire appel au service Web pour créer une exception. Vous êtes libre de le faire dans n'importe quelle situation exceptionnelle (comme dans le cas où vous devez échouer rapidement) - tout est à votre discrétion.

2
Petr Macek

Les exceptions sont destinées aux événements qui sont des comportements anormaux, des erreurs, des échecs, etc. Le comportement fonctionnel, les erreurs d'utilisateur, etc., devraient plutôt être traités par la logique de programme. Dans la mesure où un compte ou un mot de passe incorrect fait partie du flux logique d'une routine de connexion, il devrait être en mesure de gérer ces situations sans exception.

2
Joe Skora

En général, vous voulez lever une exception pour tout ce qui peut arriver dans votre application et qui est "Exceptionnel".

Dans votre exemple, ces deux exceptions donnent l'impression que vous les appelez via une validation de mot de passe/nom d'utilisateur. Dans ce cas, on peut faire valoir qu'il n'est pas vraiment exceptionnel que quelqu'un saisisse un nom d'utilisateur/mot de passe avec erreur.

Ce sont des "exceptions" au flux principal de votre langage UML, mais davantage de "branches" dans le traitement.

Si vous tentiez d'accéder à votre fichier passwd ou à votre base de données sans y parvenir, il s'agirait d'un cas exceptionnel et justifierait le lancement d'une exception.

2
Gord

Premièrement, si les utilisateurs de votre API ne sont pas intéressés par des échecs spécifiques à granularité fine, il ne sert à rien d'avoir des exceptions spécifiques.

Dans la mesure où il est souvent impossible de savoir ce qui peut être utile pour vos utilisateurs, une meilleure approche consiste à définir des exceptions spécifiques, tout en veillant à ce qu'elles héritent d'une classe commune (par exemple, std :: exception ou ses dérivés en C++). Cela permet à votre client d'attraper des exceptions spécifiques s'il le souhaite ou l'exception plus générale s'il ne s'en soucie pas.

2
Jason Etheridge

J'ai des problèmes philosophiques avec l'utilisation des exceptions. Fondamentalement, vous vous attendez à ce qu'un scénario spécifique se produise, mais plutôt que de le gérer explicitement, vous repoussez le problème pour qu'il soit traité "ailleurs". Et où se trouve cet "ailleurs" peut-on deviner.

2
Dan

J'ai trois types de conditions que j'attrape.

  1. Une entrée incorrecte ou manquante ne doit pas être une exception. Utilisez js côté client et regex côté serveur pour détecter, définir les attributs et les renvoyer à la même page avec des messages.

  2. Le AppException. Il s’agit généralement d’une exception que vous détectez et intégrez dans votre code. En d'autres termes, ce sont ceux que vous attendez (le fichier n'existe pas). Consignez-le, configurez le message et renvoyez-le à la page d'erreur générale. Cette page contient généralement quelques informations sur ce qui s'est passé.

  3. L'exception inattendue. Ce sont ceux que vous ne connaissez pas. Consignez-le avec les détails et transmettez-les à une page d'erreur générale.

J'espère que cela t'aides

1
Michael

pour moi, une exception doit être levée lorsqu'une règle technique ou métier requise échoue. Par exemple, si une entité automobile est associée à un ensemble de 4 pneus ... si un ou plusieurs pneus sont nuls ... une exception doit être déclenchée "NotEnoughTiresException", car elle peut être interceptée à différents niveaux du système et avoir un impact significatif. ce qui signifie à travers la journalisation. En outre, si nous essayons simplement de contrôler le zéro et d'éviter l'instanciation de la voiture. nous pourrions ne jamais trouver la source du problème, car le pneu n’est pas censé être nul en premier lieu.

1
Genjuro

La réponse simple est qu’une opération est impossible (à cause de l’application OR car elle enfreindrait la logique d’entreprise). Si une méthode est invoquée et qu'il est impossible de faire ce pour quoi la méthode a été écrite, lancez une exception. Un bon exemple est que les constructeurs lèvent toujours ArgumentExceptions si une instance ne peut pas être créée à l'aide des paramètres fournis. Un autre exemple est InvalidOperationException, qui est levé lorsqu'une opération ne peut pas être effectuée en raison de l'état d'un autre membre ou des membres de la classe.

Dans votre cas, si une méthode telle que Login (nom d'utilisateur, mot de passe) est invoquée, si le nom d'utilisateur n'est pas valide, il est en effet correct de lancer une exception UserNameNotValidException ou PasswordNotCorrectException si le mot de passe est incorrect. L’utilisateur ne peut pas se connecter à l’aide du ou des paramètres fournis (c’est-à-dire qu’il est impossible, car cela violerait l’authentification), lancez donc une exception. Bien que mes deux exceptions puissent hériter de ArgumentException.

Cela dit, si vous ne souhaitez PAS lancer d'exception, car un échec de connexion peut être très courant, une stratégie consiste à créer une méthode renvoyant des types qui représentent des échecs différents. Voici un exemple:

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

On apprend à la plupart des développeurs à éviter les exceptions en raison des frais généraux occasionnés par leur lancement. C'est génial d'être soucieux des ressources, mais généralement pas au détriment de la conception de votre application. C'est probablement la raison pour laquelle on vous a dit de ne pas lancer vos deux exceptions. L'utilisation d'exceptions ou non dépend généralement de la fréquence à laquelle l'exception se produira. S'il s'agit d'un résultat assez commun ou assez prévisible, la plupart des développeurs éviteront Exceptions et créeront une autre méthode d'indication d'échec, en raison de la supposée consommation de ressources.

Voici un exemple pour éviter d'utiliser Exceptions dans un scénario comme décrit ci-dessus, à l'aide du modèle Try ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}
1
core

À mon sens, la question fondamentale devrait être de savoir si l’on pourrait s’attendre à ce que l’appelant souhaite poursuivre le déroulement normal du programme si une condition survient. Si vous ne le savez pas, utilisez des méthodes doSomething et trySomething distinctes, la première renvoyant une erreur et la dernière, ou une routine acceptant un paramètre indiquant si une exception doit être levée en cas d'échec. Envisagez une classe pour envoyer des commandes à un système distant et signaler les réponses. Certaines commandes (par exemple, redémarrer) obligeront le système distant à envoyer une réponse, mais ne répondront plus pendant un certain laps de temps. Il est donc utile de pouvoir envoyer une commande "ping" et savoir si le système distant répond dans un délai raisonnable sans avoir à lever une exception s'il ne le fait pas (l'appelant s'attendrait probablement à ce que les " ping "les tentatives échoueraient, mais on finirait par travailler). Par contre, si on a une séquence de commandes comme:

 exchange_command ("open tempfile"); 
 exchange_command ("write tempfile data {any}}"); 
 exchange_command ("write tempfile data {any}}"); 
 exchange_command ("écrire des données tempfichier {quel que soit}"); 
 exchange_command ("écrire des données tempfile {quel que soit}"); 
 exchange_command ("fermer fichier temp" ";; ("copie le fichier temporaire dans le fichier réel"); 

on voudrait que l’échec de toute opération annule toute la séquence. Bien que l’on puisse vérifier chaque opération pour s’assurer de son succès, il est plus utile que la routine exchange_command () lève une exception si une commande échoue.

En fait, dans le scénario ci-dessus, il peut être utile d’avoir un paramètre permettant de sélectionner un certain nombre de modes de gestion des défaillances: ne jamais lancer d’exceptions, uniquement des erreurs de communication ou des exceptions dans les cas où une commande ne renvoie pas "succès". "indication.

1
supercat

la principale raison d'éviter de lancer une exception est qu'il y a beaucoup de frais généraux impliqués dans le lancement d'une exception.

L'article ci-dessous indique notamment qu'une exception concerne des conditions et des erreurs exceptionnelles.

Un nom d'utilisateur incorrect n'est pas nécessairement une erreur de programme, mais une erreur d'utilisateur ...

Voici un bon point de départ pour les exceptions dans .NET: http://msdn.Microsoft.com/fr-fr/library/ms229030 (VS.80) .aspx

1
Sam

La sécurité est confondue avec votre exemple: vous ne devriez pas dire à un attaquant qu’un nom d’utilisateur existe, mais que le mot de passe est incorrect. C'est une information supplémentaire que vous n'avez pas besoin de partager. Il suffit de dire "le nom d'utilisateur ou mot de passe est incorrect."

1
anon

Le lancement d'exceptions provoque le déchargement de la pile, ce qui a des conséquences en termes de performances (les environnements gérés modernes l'ont bien compris). Toujours lancer et capturer à plusieurs reprises des exceptions dans une situation imbriquée serait une mauvaise idée.

Probablement plus important que cela, les exceptions sont destinées à des conditions exceptionnelles. Ils ne doivent pas être utilisés pour un flux de contrôle ordinaire, car cela nuirait à la lisibilité de votre code.

1
Arno

Quelques éléments utiles à prendre en compte pour décider si une exception est appropriée:

  1. quel niveau de code vous souhaitez exécuter après l'apparition de l'exception candidate, c'est-à-dire combien de couches de la pile d'appels doivent se dérouler. Vous souhaitez généralement gérer une exception aussi près que possible de l'endroit où elle se produit. Pour la validation du nom d'utilisateur/mot de passe, vous devez normalement gérer les échecs dans le même bloc de code, plutôt que de laisser une exception bouillonner. Donc, une exception n'est probablement pas appropriée. (OTOH, après trois tentatives de connexion infructueuses, le flux de contrôle peut être déplacé ailleurs et une exception peut être appropriée ici.)

  2. Cet événement est-il quelque chose que vous voudriez voir dans un journal des erreurs? Toutes les exceptions ne sont pas écrites dans un journal des erreurs, mais il est utile de demander si cette entrée dans un journal des erreurs serait utile - c’est-à-dire que vous tenteriez de faire quelque chose à ce sujet ou qu’il s’agirait d’un fouillis que vous ignorez.

0
Mike Kantor

Il existe deux classes principales d'exception:

1) Exception système (par exemple, perte de connexion à la base de données) ou 2) Exception utilisateur. (p. ex. validation de la saisie de l'utilisateur, "le mot de passe est incorrect")

J'ai trouvé utile de créer ma propre classe d'exception utilisateur et lorsque je souhaite émettre une erreur utilisateur, je veux être traité différemment (c.-à-d. Une erreur affectée à une ressource affichée à l'utilisateur), il ne me reste plus qu'à vérifier dans le type d'objet :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If
0
Crusty

"PasswordNotCorrectException" n'est pas un bon exemple d'utilisation des exceptions. Les utilisateurs se tromper de mots de passe est à prévoir, donc ce n'est guère une exception IMHO. Vous avez probablement même récupéré, en affichant un message d'erreur Nice, donc c'est juste une vérification de validité.

Les exceptions non gérées arrêteront éventuellement l'exécution - ce qui est bien. Si vous renvoyez des codes faux, nuls ou d'erreur, vous devrez gérer vous-même l'état du programme. Si vous oubliez de vérifier les conditions quelque part, votre programme peut continuer à fonctionner avec des données erronées et vous aurez peut-être de la difficulté à déterminer quoi s'est passé et .

Bien sûr, vous pouvez poser le même problème avec les instructions de capture vides, mais au moins, les repérer est plus facile et ne nécessite pas de comprendre la logique.

Donc, en règle générale:

Utilisez-les où vous ne voulez pas ou ne pouvez tout simplement pas récupérer d'une erreur.

0
DanMan

Vous pouvez utiliser un peu des exceptions génériques pour ces conditions. Par exemple ArgumentException est destiné à être utilisé en cas de problème avec les paramètres d'une méthode (à l'exception de ArgumentNullException). En règle générale, vous n'avez pas besoin d'exceptions comme LessThanZeroException, NotPrimeNumberException, etc. Pensez à l'utilisateur de votre méthode. Le nombre de conditions qu’elle voudra gérer spécifiquement est égal au nombre du type des exceptions que votre méthode doit générer. De cette façon, vous pouvez déterminer le niveau de détail des exceptions.

En passant, essayez toujours de fournir des moyens aux utilisateurs de vos bibliothèques pour éviter les exceptions. TryParse est un bon exemple, il existe pour que vous n'ayez pas à utiliser int.Parse et attraper une exception. Dans votre cas, vous souhaiterez peut-être indiquer des méthodes permettant de vérifier si le nom d'utilisateur est valide ou le mot de passe correct, afin que vos utilisateurs (ou vous-même) n'aient pas à gérer beaucoup d'exceptions. Espérons que cela aboutira à un code plus lisible et à de meilleures performances.

0
Serhat Ozgel

En fin de compte, la décision revient à savoir s’il est plus utile de traiter des erreurs au niveau de l’application telles que celle-ci à l’aide de la gestion des exceptions ou via votre propre mécanisme, tel que le renvoi des codes d’état. Je ne pense pas qu'il y ait une règle stricte sur ce qui est le meilleur, mais je considérerais:

  • Qui appelle votre code? S'agit-il d'une API publique ou d'une bibliothèque interne?
  • Quelle langue utilisez-vous? S'il s'agit de Java, par exemple, le lancement d'une exception (cochée) impose à votre appelant le fardeau explicite de gérer cette condition d'erreur d'une manière ou d'une autre, par opposition à un statut de retour qui pourrait être ignoré. Cela pourrait être bon ou mauvais.
  • Comment les autres conditions d'erreur dans la même application sont-elles gérées? Les appelants ne voudront pas traiter avec un module qui traite les erreurs d'une manière idiosyncratique, comme nulle part ailleurs dans le système.
  • Combien de problèmes peuvent survenir avec la routine en question et comment seraient-ils gérés différemment? Considérez la différence entre une série de blocs catch manipulant différentes erreurs et un changement de code d'erreur.
  • Avez-vous des informations structurées sur l'erreur que vous devez retourner? Lancer une exception vous donne un meilleur endroit pour mettre cette information que de simplement retourner un statut.
0
eli