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 devais pas créer d’exceptions pour ces conditions. Dans mon langage UML, il existe des exceptions au stream principal, alors pourquoi ne pas en faire une exception?

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

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 qui est 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 lancer d’exception, car il n’y a pas de zone grise dans son fonctionnement – chaque classe hérite ou non de List <>, donc la réponse est toujours “yes” ou “no”.

Exemple 2: disons que j’ai une autre fonction qui examine une liste <> et retourne true si sa longueur est supérieure à 50, et false si sa longueur est inférieure. Cette fonction pose la question “Cette liste contient-elle plus de 50 éléments?” Mais cette question fait une supposition – elle suppose que l’objet qui lui est donné est une liste. Si je lui donne un NULL, cette hypothèse est fausse. Dans ce cas, si la fonction renvoie true ou false, elle enfreint ses propres règles. La fonction ne peut rien renvoyer et prétendre avoir répondu correctement à la question. Donc, ça ne revient pas – ça lance une exception.

Ceci est comparable à l’erreur logique “question chargée” . Chaque fonction pose une question. Si l’entrée est donnée, cette question devient une erreur, puis lance une exception. Cette ligne est plus difficile à dessiner avec des fonctions qui renvoient des vides, mais l’essentiel est le suivant: si les suppositions de la fonction concernant ses entrées sont violées, elle devrait lancer une exception au lieu de revenir normalement.

L’autre aspect de cette équation est le suivant: si vous trouvez que vos fonctions entraînent fréquemment des exceptions, vous devrez probablement affiner leurs hypothèses.

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

Mes petites lignes direcsortingces sont fortement influencées par le grand livre “Code complete”:

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

Ce n’est PAS une exception si le nom d’utilisateur n’est pas valide ou si le mot de passe n’est pas correct. Ce sont des choses auxquelles vous devez 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 dire si une méthode lance 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 à “mémoire insuffisante” ou “ordinateur en feu”).

Une règle de base consiste à utiliser des exceptions dans le cas de quelque chose que vous ne pouviez normalement pas prévoir. Les exemples sont la connectivité de la firebase database, le 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 Vous ne voulez pas terminer brusquement l’exécution en lançant une exception simplement parce que quelqu’un a mal saisi son mot de passe.

D’autres proposent que les exceptions ne soient pas utilisées car le mauvais login doit être prévu dans un stream normal si l’utilisateur mal interprète. 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 lancée par la structure. En utilisant la logique ci-dessus, c’était une erreur de Microsoft. Ils auraient dû retourner un code d’erreur. Idem pour l’parsing, les requêtes, etc., etc.

Je ne considère pas qu’une mauvaise connexion fait partie d’un stream normal, c’est exceptionnel. Normalement, l’utilisateur saisit le mot de passe correct et le fichier existe. Les cas exceptionnels sont exceptionnels et il est parfaitement acceptable d’utiliser des exceptions pour ceux-ci. Compliquer votre code en propageant les valeurs de retour à travers les n niveaux de la stack est un gaspillage d’énergie et entraînera un code désordonné. Faites la chose la plus simple qui pourrait éventuellement fonctionner. N’optimisez pas prématurément en utilisant des codes d’erreur, des choses exceptionnelles se produisent rarement et les exceptions ne coûtent rien à moins que vous ne les lanciez.

Les exceptions sont un effet assez coûteux, si par exemple vous avez un utilisateur qui fournit un mot de passe non valide, il est généralement préférable de repasser un indicateur d’échec, ou un autre indicateur d’invalidité.

Cela est dû à la façon dont les exceptions sont gérées, à la véritable saisie incorrecte et aux éléments d’arrêt critiques uniques qui doivent être des exceptions, mais pas des informations de connexion ayant échoué.

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


Vous verrez beaucoup de conseils, y compris dans les réponses à cette question, que vous devriez jeter des exceptions seulement dans des circonstances “exceptionnelles”. Cela semble superficiellement raisonnable, mais c’est un conseil erroné, car il remplace une question (“quand devrais-je lancer une exception”) par une autre question subjective (“ce qui est exceptionnel”). Au lieu de cela, suivez les conseils de Herb Sutter (pour C ++, disponible dans l’ article de Dr Dobbs, Quand et comment utiliser les exceptions , et aussi dans son livre avec Andrei Alexandrescu, Normes de codage C ++ ): lancez une exception si, et seulement si

  • une condition préalable n’est pas remplie (ce qui rend généralement l’une des situations suivantes impossible) ou
  • la solution de rechange ne répondrait pas à une condition postérieure 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 conditions préalables, les postconditions et les invariants? C’est mieux pour plusieurs raisons liées.

  • Les conditions préalables, les postconditions et les invariants sont des caractéristiques de conception de notre programme (son API interne), alors que la décision de throw est un détail d’implémentation. Cela nous oblige à garder à l’esprit que nous devons considérer la conception et son implémentation séparément, et notre travail lors de la 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, de postconditions et d’invariants, qui sont les seules hypothèses que les appelants de notre méthode devraient formuler, et qui sont exprimés précisément, permettant un couplage libre entre les composants de notre programme.
  • Ce couplage lâche nous permet ensuite de refactoriser la mise en œuvre, si nécessaire.
  • Les post-conditions et invariants sont testables; Il en résulte un code facilement testable 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 du succès en tant que post-condition , ce qui est le style naturel pour utiliser des exceptions. Le chemin d’exécution normal (“heureux”) de votre programme est disposé de manière linéaire, avec tout le code de gestion des erreurs déplacé vers les clauses catch .

Je dirais qu’il n’y a pas de règles ssortingctes sur le moment d’utiliser les exceptions. Cependant, il y a de bonnes raisons de les utiliser ou non:

Raisons d’utiliser des exceptions:

  • Le stream de code pour le cas commun est plus clair
  • Peut renvoyer des informations d’erreur complexes en tant qu’object (bien que cela puisse également être réalisé en utilisant le paramètre d’erreur “out” transmis par référence)
  • Les langues offrent généralement une certaine facilité pour gérer le nettoyage en cas d’exception (essayez / finalement en Java, en utilisant C #, RAII en C ++)
  • Dans le cas où 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 capturées (bien que cela puisse constituer une raison)

Raisons pour ne pas utiliser les 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 retour (sortie applicative vs échec silencieux – qui 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 ou ne les attrapez pas, mais appelez indirectement une fonction de lancement)
  • En C ++, il est difficile de savoir quand une fonction peut être lancée. 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 beaucoup plus coûteux que de vérifier un indicateur de retour

En général, je serais plus enclin à utiliser des exceptions en 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, car la modification de votre garantie d’exception peut casser le code d’appel. Le plus grand avantage de leur utilisation dans Java IMO est que vous savez que votre appelant DOIT gérer l’exception, ce qui augmente les chances de comportement correct.

De ce fait, dans n’importe quel langage, je dériverais toujours toutes les exceptions dans une couche de code ou une API à partir d’une classe commune, de sorte que le code d’appel puisse toujours garantir que toutes les exceptions seront détectées. Aussi, je considérerais comme mauvais de lancer des classes d’exception spécifiques à l’implémentation lors de l’écriture d’une API ou d’une bibliothèque (par exemple, en intégrant les exceptions des couches inférieures pour que l’exception reçue par l’appelant soit compréhensible dans le contexte de votre interface).

Notez que Java fait la distinction entre les exceptions générales et les exceptions Runtime dans la mesure où ces dernières ne doivent pas être déclarées. J’utiliserais uniquement les classes d’exception Runtime lorsque vous savez que l’erreur est le résultat d’un bogue dans le programme.

Les classes d’exception sont comme les classes “normales”. Vous créez une nouvelle classe lorsqu’elle “est” un type d’object différent, avec des champs différents et des opérations différentes.

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

Si le code s’exécute dans une boucle qui provoquera probablement une exception encore et encore, lancer des exceptions n’est pas une bonne chose, car elles sont assez lentes pour un grand N. Mais il n’y a rien de mal à générer des exceptions personnalisées si les performances ne sont pas bonnes. un problème. Assurez-vous simplement d’avoir 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 ne pas être excessif.

Donc, la réponse courte est que si cela ne cause pas une pénalité de performance significative (ce qui ne devrait pas être le cas sauf si vous lancez beaucoup d’exceptions), alors allez-y.

Je suis d’accord avec japollock là-haut – jetez un regard quand vous n’êtes pas certain du résultat d’une opération. Appels aux API, access aux systèmes de fichiers, appels de firebase database, etc. Chaque fois que vous dépassez les “limites” de vos langages de programmation.

J’aimerais append, n’hésitez pas à lancer une exception standard. À moins que vous ne fassiez quelque chose de “différent” (ignorer, envoyer des courriels, vous connecter, montrer ce que vous avez vu, etc.), ne vous souciez pas des exceptions personnalisées.

la règle de base pour lancer des exceptions est assez simple. vous le faites lorsque votre code est entré dans un état INVERSABLE INRECOVERABLE. Si les données sont compromises ou que vous ne pouvez pas rétablir le traitement qui a eu lieu, vous devez le terminer. en effet quoi d’autre pouvez-vous faire? 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 lancez pas d’exception.

Dans votre cas particulier, si vous avez été forcé de faire quelque chose de stupide, comme accepter un retrait d’argent, et puis seulement vérifier user / pasword, vous devez terminer le processus en envoyant une exception pour signaler que quelque chose de grave s’est produit.

En général, vous voulez lancer une exception pour tout ce qui peut arriver dans votre application qui est “exceptionnel”

Dans votre exemple, ces deux exceptions semblent vous appeler via une validation de mot de passe / nom d’utilisateur. Dans ce cas, on peut affirmer que ce n’est pas vraiment exceptionnel que quelqu’un prenne un nom d’utilisateur / mot de passe.

Ce sont des “exceptions” au stream principal de votre UML mais sont plus de “twigs” dans le traitement.

Si vous tentiez d’accéder à votre fichier ou à votre firebase database passwd et que vous ne le pouviez pas, cela constituerait un cas exceptionnel et justifierait une exception.

Premièrement, si les utilisateurs de votre API ne sont pas intéressés par des échecs précis et précis, il est inutile d’avoir des exceptions spécifiques.

Comme il est souvent impossible de savoir ce qui peut être utile à vos utilisateurs, une meilleure approche consiste à avoir les exceptions spécifiques, mais assurez-vous 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’ils s’en foutent.

Les exceptions sont destinées à des événements qui sont des comportements anormaux, des erreurs, des échecs, etc. Le comportement fonctionnel, l’erreur de l’utilisateur, etc. doivent plutôt être gérés par la logique du programme. Comme un compte ou un mot de passe incorrect est une partie attendue du stream logique dans une routine de connexion, il doit pouvoir gérer ces situations sans exception.

J’ai des problèmes philosophiques avec l’utilisation des exceptions. Fondamentalement, vous vous attendez à un scénario spécifique, mais plutôt que de le traiter explicitement, vous poussez le problème pour qu’il soit traité «ailleurs». Et où ce “ailleurs” est peut être la conjecture de n’importe qui.

Je dirais que généralement tous les fondamentalismes mènent à l’enfer.

Vous ne voudrez certainement pas vous retrouver avec un stream impulsé par les exceptions, mais éviter les exceptions est également une mauvaise idée. Vous devez trouver un équilibre entre les deux approches. Ce que je ne ferais pas, 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 créer deux types d’exceptions de base utilisés dans tout le système: LogicalException et TechnicalException . Ceux-ci peuvent être distingués par des sous-types si nécessaire, mais ce n’est généralement pas nécessaire.

L’exception technique dénote l’exception vraiment inattendue, telle que l’arrêt du serveur de firebase database, la connexion au service Web a entraîné l’exception IOException, etc.

Par contre, les exceptions logiques sont utilisées pour propager la situation erronée la 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 déroulement du programme, mais plutôt pour mettre en évidence la situation dans laquelle le stream devrait réellement prendre fin. Lorsqu’elles sont utilisées en Java, les deux types d’exceptions sont les sous-classes RuntimeException et la gestion des erreurs est fortement orientée vers l’aspect.

Ainsi, dans l’exemple de connexion, il peut être judicieux de créer une authentification comme AuthenticationException et de distinguer les situations concrètes par des valeurs telles que UsernameNotExisting , PasswordMismatch, etc. . Vous pouvez aussi facilement utiliser un mécanisme générique de gestion des exceptions, car vous disposez des exceptions classées par catégories et savez très bien ce que vous devez propager à l’utilisateur et comment.

Notre utilisation typique consiste à lancer une exception LogicalException pendant l’appel du service Web lorsque la saisie de l’utilisateur n’est pas valide. L’exception est ordonnée au détail de SOAPFault, puis est à nouveau déréglée à l’exception sur le client, ce qui entraîne l’affichage de l’erreur de validation sur un certain champ de saisie de page Web, étant donné que cette exception correspond à ce champ.

Ce n’est certainement pas la seule situation: vous n’avez pas besoin d’accéder au service Web pour créer une exception. Vous êtes libre de le faire dans toutes les situations exceptionnelles (comme dans le cas où vous devez effectuer une erreur) – tout est à votre discrétion.

La principale raison pour éviter de lancer une exception est que le fait de lancer une exception est très contraignant.

Une chose que l’article ci-dessous stipule est une exception pour des conditions et des erreurs exceptionnelles.

Un nom d’utilisateur erroné n’est pas nécessairement une erreur de programme mais une erreur d’utilisateur …

Voici un bon sharepoint départ pour les exceptions dans .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx

Le fait de lancer des exceptions entraîne le déroulement de la stack, ce qui a des conséquences sur les performances (les environnements gérés modernes ont été améliorés). Jeter et attraper encore des exceptions dans une situation nestede 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 stream de contrôle ordinaire, car cela nuirait à la lisibilité de votre code.

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 des atsortingbuts et renvoyer à la même page avec des messages.

  2. L’AppException. C’est généralement une exception que vous détectez et jetez avec votre code. En d’autres termes, ce sont ceux que vous attendez (le fichier n’existe pas). Enregistrez-le, définissez le message et revenez à 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. Enregistrez-le avec des détails et transférez-les vers une page d’erreur générale.

J’espère que cela t’aides

La sécurité est associée à votre exemple: vous ne devez pas dire à un attaquant qu’un nom d’utilisateur existe, mais le mot de passe est incorrect. C’est des informations supplémentaires que vous n’avez pas besoin de partager. Dites simplement “le nom d’utilisateur ou le mot de passe est incorrect.”

La réponse simple est que chaque fois qu’une opération est impossible (à cause de l’une des applications OU parce qu’elle violerait la logique métier). Si une méthode est invoquée et qu’il est impossible de faire ce que la méthode a écrit, lancez une exception. Un bon exemple est que les constructeurs lancent toujours des ArgumentExceptions si une instance ne peut pas être créée à l’aide des parameters fournis. Un autre exemple est InvalidOperationException, qui est lancé lorsqu’une opération ne peut pas être effectuée en raison de l’état d’un ou de plusieurs membres de la classe.

Dans votre cas, si une méthode comme Login (nom d’utilisateur, mot de passe) est appelé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 être connecté à l’aide du ou des parameters fournis (c.-à-d. Qu’il est impossible car cela violerait l’authentification), alors lancez une exception. Bien que je puisse avoir vos deux exceptions hériter de ArgumentException.

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

 { // class ... public LoginResult Login(ssortingng user, ssortingng 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 ssortingng Message; protected LoginResult(ssortingng message) { this.Message = message; } } public class SuccessfulLoginResult : LoginResult { public SucccessfulLogin(ssortingng user) : base(ssortingng.Format("Login for user '{0}' was successful.", user)) { } } public class UserInvalidLoginResult : LoginResult { public UserInvalidLoginResult(ssortingng user) : base(ssortingng.Format("The username '{0}' is invalid.", user)) { } } public class PasswordInvalidLoginResult : LoginResult { public PasswordInvalidLoginResult(ssortingng password, ssortingng user) : base(ssortingng.Format("The password '{0}' for username '{0}' is invalid.", password, user)) { } } 

La plupart des développeurs apprennent à éviter les exceptions en raison de la surcharge engendrée par leur projection. C’est génial d’être conscient des ressources, mais généralement pas au désortingment de la conception de votre application. C’est probablement la raison pour laquelle on vous a dit de ne pas jeter vos deux exceptions. Le recours aux exceptions ou non se résume généralement à la fréquence à laquelle l’exception se produira. Si c’est un résultat assez commun ou assez attendu, la plupart des développeurs évitent les exceptions et créent une autre méthode pour indiquer un échec, en raison de la consommation supposée de ressources.

Voici un exemple d’évitement de l’utilisation des exceptions dans un scénario comme décrit précédemment, en utilisant le modèle Try ():

 public class ValidatedLogin { public readonly ssortingng User; public readonly ssortingng Password; public ValidatedLogin(ssortingng user, ssortingng 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(ssortingng user, ssortingng password, out ValidatedLogin validatedLogin) { if (IsInvalidUser(user) || IsInvalidPassword(user, password)) { return false; } validatedLogin = new ValidatedLogin(user, password); return true; } } 

To my mind, the fundamental question should be whether one would expect that the caller would want to continue normal program flow if a condition occurs. If you don’t know, either have separate doSomething and trySomething methods, where the former returns an error and the latter does not, or have a routine that accepts a parameter to indicate whether an exception should be thrown if it fails). Consider a class to send commands to a remote system and report responses. Certain commands (eg restart) will cause the remote system to send a response but then be non-responsive for a certain length of time. It is thus useful to be able to send a “ping” command and find out whether the remote system responds in a reasonable length of time without having to throw an exception if it doesn’t (the caller would probably expect that the first few “ping” attempts would fail, but one would eventually work). On the other hand, if one has a sequence of commands like:

  exchange_command("open tempfile");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("close tempfile");
  exchange_command("copy tempfile to realfile");

one would want failure of any operation to abort the whole sequence. While one could check each operation to ensure it succeeded, it’s more helpful to have the exchange_command() routine throw an exception if a command fails.

Actually, in the above scenario it may be helpful to have a parameter to select a number of failure-handling modes: never throw exceptions, throw exceptions for communication errors only, or throw exceptions in any cases where a command does not return a “success” indication.

for me Exception should be thrown when a required technical or business rule fails. for instance if a car entity is associated with array of 4 tires … if one tire or more are null … an exception should be Fired “NotEnoughTiresException” , cuz it can be caught at different level of the system and have a significant meaning through logging. besides if we just try to flow control the null and prevent the instanciation of the car . we might never never find the source of the problem , cuz the tire isn’t supposed to be null in the first place .

You may use a little bit generic exceptions for that conditions. For eg ArgumentException is meant to be used when anything goes wrong with the parameters to a method (with the exception of ArgumentNullException). Generally you would not need exceptions like LessThanZeroException, NotPrimeNumberException etc. Think of the user of your method. The number of the conditions that she will want to handle specifically is equal to the number of the type of the exceptions that your method needs to throw. This way, you can determine how detailed exceptions you will have.

By the way, always try to provide some ways for users of your libraries to avoid exceptions. TryParse is a good example, it exists so that you don’t have to use int.Parse and catch an exception. In your case, you may want to provide some methods to check if user name is valid or password is correct so your users (or you) will not have to do lots of exception handling. This will hopefully result in more readble code and better performance.

Ultimately the decision comes down to whether it is more helpful to deal with application-level errors like this using exception handling, or via your own home-rolled mechanism like returning status codes. I don’t think there’s a hard-and-fast rule about which is better, but I would consider:

  • Who’s calling your code? Is this a public API of some sort or an internal library?
  • What language are you using? If it’s Java, for example, then throwing a (checked) exception puts an explicit burden on your caller to handle this error condition in some way, as opposed to a return status which could be ignored. That could be good or bad.
  • How are other error conditions in the same application handled? Callers won’t want to deal with a module that handles errors in an idiosyncratic way unlike anything else in the system.
  • How many things can go wrong with the routine in question, and how would they be handled differently? Consider the difference between a series of catch blocks that handle different errors and a switch on an error code.
  • Do you have structured information about the error you need to return? Throwing an exception gives you a better place to put this information than just returning a status.

There are two main classes of exception:

1) System exception (eg Database connection lost) or 2) User exception. (eg User input validation, ‘password is incorrect’)

I found it helpful to create my own User Exception Class and when I want to throw a user error I want to be handled differently (ie resourced error displayed to the user) then all I need do in my main error handler is check the object type:

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

Some useful things to think about when deciding whether an exception is appropriate:

  1. what level of code you want to have run after the exception candidate occurs – that is, how many layers of the call stack should unwind. You generally want to handle an exception as close as possible to where it occurs. For username/password validation, you would normally handle failures in the same block of code, rather than letting an exception bubble up. So an exception is probably not appropriate. (OTOH, after three failed login attempts, control flow may shift elsewhere, and an exception may be appropriate here.)

  2. Is this event something you would want to see in an error log? Not every exception is written to an error log, but it’s useful to ask whether this entry in an error log would be useful – ie, you would try to do something about it, or would be the garbage you ignore.

“PasswordNotCorrectException” isn’t a good example for using exceptions. Users getting their passwords wrong is to be expected, so it’s hardly an exception IMHO. You probably even recover from it, showing a nice error message, so it’s just a validity check.

Unhandled exceptions will stop the execution eventually – which is good. If you’re returning false, null or error codes, you will have to deal with the program’s state all by yourself. If you forget to check conditions somewhere, your program may keep running with wrong data, and you may have a hard time figuring out what happened and where .

Of course, you could cause the same problem with empty catch statements, but at least spotting those is easier and doesn’t require you to understand the logic.

So as a rule of thumb:

Use them wherever you don’t want or simply can’t recover from an error.