Lancer / ne pas lancer une exception basée sur un paramètre - pourquoi n'est-ce pas une bonne idée?

StackOverflow https://stackoverflow.com/questions/624647

Question

Je cherchais dans MSDN et trouvais cet article qui a émis un conseil intéressant: ne pas avoir de membres du public pouvant ou non lancer des exceptions en fonction de certaines options.

Par exemple:

Uri ParseUri(string uriValue, bool throwOnError)

Maintenant, bien sûr, je peux voir que dans 99% des cas, ce serait horrible, mais son utilisation occasionnelle est-elle justifiée?

L’un des cas que j’ai vu utilisé est avec un " AllowEmpty " paramètre lors de l'accès aux données de la base de données ou d'un fichier de configuration. Par exemple:

object LoadConfigSetting(string key, bool allowEmpty);

Dans ce cas, l’alternative serait de renvoyer null. Mais alors le code appelant serait jonché de références nulles. (Et la méthode exclurait également la possibilité d’autoriser réellement la valeur null comme valeur spécifiquement configurable, si cela vous tenait à coeur.)

Quelles sont vos pensées? Pourquoi serait-ce un gros problème?

Était-ce utile?

La solution

Je pense que c’est définitivement une mauvaise idée que la décision de lancer / non de lancer soit basée sur un booléen. En effet, les développeurs qui recherchent un code doivent avoir une connaissance fonctionnelle de l'API pour déterminer la signification du booléen. C'est mauvais en soi, mais quand il change la gestion des erreurs sous-jacentes, les développeurs peuvent facilement faire des erreurs en lisant du code.

Il serait bien mieux et plus lisible d’avoir 2 API dans ce cas.

Uri ParseUriOrThrow(string value);

bool TryParseUri(string value, out Uri uri);

Dans ce cas, le fonctionnement de ces API est parfaitement clair.

Article sur les raisons pour lesquelles les booléens sont mauvais en tant que paramètres: http : //blogs.msdn.com/jaredpar/archive/2007/01/23/boolean-parameters.aspx

Autres conseils

Il est généralement préférable de choisir un mécanisme de traitement des erreurs et de le respecter systématiquement. Autoriser ce type de code de bascule ne peut pas vraiment améliorer la vie des développeurs.

Dans l'exemple ci-dessus, que se passe-t-il si l'analyse échoue et si throwOnError est false? Maintenant, l’utilisateur doit deviner si NULL est renvoyé, ou dieu sait ...

Certes, il existe un débat en cours entre les exceptions et les valeurs renvoyées en tant que meilleure méthode de gestion des erreurs, mais je suis à peu près sûr qu'il existe un consensus sur la cohérence et le respect du choix que vous faites. L'API ne peut pas surprendre ses utilisateurs et la gestion des erreurs doit faire partie de l'interface et être définie aussi clairement que l'interface.

C'est un peu méchant du point de vue de la lisibilité. Les développeurs ont tendance à s'attendre à ce que chaque méthode lève une exception et s'ils l'ignorent, ils l'attrapent eux-mêmes. Avec l'approche "drapeau booléen", chaque méthode doit implémenter cette sémantique inhibitrice d'exception.

Cependant, je pense que l'article de MSDN fait strictement référence aux indicateurs "throwOnError". Dans ces cas, l'erreur est ignorée dans la méthode elle-même (mauvaise, car elle est masquée) ou un type d'objet null / error est renvoyé (mauvais, car vous n'utilisez pas d'exceptions pour gérer l'erreur, qui est incohérente et proprement l'erreur. -prone).

Alors que votre exemple me semble bien. Une exception indique un échec de la méthode dans l'exécution de sa tâche - il n'y a pas de valeur de retour. Cependant, l'indicateur 'allowEmpty' modifie la sémantique de la méthode - de sorte que ce qui aurait été une exception ('valeur vide') est maintenant attendu et légal. De plus, si vous avez lancé une exception, vous ne pourrez pas facilement renvoyer les données de configuration. Cela semble donc bien dans ce cas.

Dans toute API publique, il est vraiment déconseillé de disposer de deux moyens de vérifier une condition défectueuse, car il devient alors moins évident de savoir ce qui se passera si l'erreur se produit. Juste en regardant le code ne va pas aider. Vous devez comprendre la sémantique du paramètre flag (et rien ne l’empêche d’être une expression).

Si la vérification de la valeur NULL n’est pas une option et si je dois remédier à cet échec spécifique, je préfère créer une exception spécifique afin de pouvoir l’attaquer ultérieurement et la gérer de manière appropriée. Dans tous les autres cas, je lève une exception générale.

Un autre exemple pourrait être défini avec les méthodes TryParse sur certains types de valeur

booléen DateTime.TryParse (chaîne de caractères, dateHeure)

Avoir un paramètre donTThrowException détruit le point entier des exceptions (dans toutes les langues). Si le code appelant veut avoir:

public static void Main()
{
        FileStream myFile = File.Open("NonExistent.txt", FileMode.Open, FileAccess.Read);
}

ils sont les bienvenus (C # n’a même pas vérifié les exceptions). En Java, la même chose serait accomplie avec:

public static void main(String[] args) throws FileNotFoundException
{
        FileInputStream fs = new FileInputStream("NonExistent.txt");
}

Quoi qu'il en soit, c'est à l'appelant de décider comment gérer (ou non) l'exception, pas l'appelé.

Dans l'article lié à l'article, il est indiqué que Exceptions ne doit pas être utilisé pour un flux de contrôle - ce qui semble être implicite dans les exemples de questions. Les exceptions doivent refléter un échec du niveau de la méthode. Avoir une signature qu'il est acceptable de jeter une erreur semble que la conception n'est pas pensée.

Le livre CLR de Jeffrey Richters via C # souligne - "vous devriez lancer une exception lorsque la méthode ne peut pas mener à bien sa tâche, comme indiqué par son nom".

Son livre a également souligné une erreur très commune. Les gens ont tendance à écrire du code pour tout attraper (ses mots "Une erreur omniprésente de développeurs qui n’ont pas été correctement formés à l’utilisation correcte des exceptions ont tendance à utiliser des blocs catch trop souvent et de manière incorrecte. Lorsque vous attrapez une exception, vous dites vous vous attendiez à cette exception, vous comprenez pourquoi elle s’est produite et vous savez comment la gérer. ")

Cela m'a fait essayer de coder des exceptions auxquelles je peux m'attendre et que je peux gérer dans ma logique, sinon cela devrait être une erreur.

Validez vos arguments et empêchez les exceptions. N'attrapez que ce que vous pouvez gérer.

Je dirais qu'il est souvent utile de disposer d'un paramètre indiquant si un échec doit provoquer une exception ou simplement renvoyer une indication d'erreur, puisqu'un tel paramètre peut facilement être passé d'une routine externe à une autre interne. Considérez quelque chose comme:

  Byte [] ReadPacket (bool DontThrowIfNone) // Documenté comme renvoyant la valeur null si aucun   {     int len ??= ReadByte (DontThrowIfNone); // Documenté comme retournant -1 si rien     si (len

Si quelque chose comme une exception TimeoutException lors de la lecture des données doit provoquer une exception, cette exception doit être levée dans ReadByte () ou ReadMultiBytesbytes (). Si, toutefois, un tel manque de données doit être considéré comme normal, la routine ReadByte () ou ReadMultiBytesbytes () ne doit pas renvoyer d'exception. Si vous utilisez simplement le modèle "faire / essayer", les routines ReadPacket et TryReadPacket doivent avoir un code presque identique, mais l'une utilisant les méthodes Read * et l'autre les méthodes TryRead *. Icky.

Il peut être préférable d’utiliser une énumération plutôt qu’un booléen.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top