web-dev-qa-db-fra.com

Renvoyer null ou une valeur vide / exception exception?

Divers livres de programmation suggèrent que les méthodes ne doivent pas retourner de valeurs null (Clean Code par exemple). Au lieu de renvoyer null les valeurs par défaut (0 ou chaîne vide ou objet vide) doivent être renvoyées ou une exception doit être levée. Ceci est recommandé afin d'éviter de nombreuses vérifications de != null Ou pour éviter NullPointerException.

Je ne comprends vraiment pas comment cela aide. Si vous renvoyez une valeur par défaut, vous devez effectuer des vérifications != DEFAULT; de plus, la valeur par défaut peut signifier quelque chose (comme 0 pour un montant de crédit). Lancer une exception spécifique ne vaut pas la peine, car si vous ne gérez pas bien votre code, une exception est quand même levée - NullPointerException.

J'y ai pensé quand j'ai trouvé un bug provoqué par une méthode qui retournait un objet vide (par exemple new MyBean()) au lieu de renvoyer null. Le code n'a pas fonctionné fonctionnellement et les journaux n'ont rien averti, donc la détection et la correction n'étaient pas si faciles; avec null un NullPointerException aurait été jeté donc repérer et corriger le bogue aurait été trivial.

Le seul cas où il est logique pour moi de retourner une valeur par défaut au lieu de renvoyer null est lors du retour de tableaux/collections; ici, ils sont généralement consommés for each element in collection donc si son vide, rien ne se passe mais si c'est null NPE est lancé.

10
m3th0dman

Divers livres de programmation suggèrent que les méthodes ne doivent pas retourner de valeurs nulles (Clean Code par exemple). Au lieu de renvoyer des valeurs par défaut nulles (0 ou chaîne vide ou objet vide) doivent être renvoyées ou une exception doit être levée. Ceci est recommandé afin d'éviter de nombreux contrôles! = Null ou pour éviter NullPointerException.

Il convient de mentionner et un malentendu commun, que M. Martin n'a pas prétendu que vous devriez toujours lever une exception et jamais renvoyer une valeur d'erreur. En fait, le passage pertinent se lit très différemment si vous le lisez attentivement (je n'ai pas la version anglaise ici, donc je dois la traduire, désolée):

Si vous êtes sur le point de renvoyer un pointeur null, vous devriez plutôt envisager de lever une exception.

Notez qu'il dit " considérez ", pas "tu dois".

Une autre bonne lecture sont les deux:


PS: texte original de mon édition allemande:

Wenn Sie versucht sind, einen Nullzeiger zu liefern, sollten Sie stattdessen erwägen, eine Exception zu werfen.

11
JensG

À mon avis, pour les méthodes de retour à valeur unique, null est de loin supérieur au retour d'une instance vide ou par défaut d'un type. Non seulement les valeurs par défaut peuvent masquer les erreurs, mais selon la complexité du type, elles peuvent créer des dépendances complexes et difficiles à documenter entre les méthodes, car les producteurs d'API et les consommateurs d'API doivent conserver une notion cohérente de ce que/quelles valeurs représentent des résultats erronés.

D'un autre côté, en ce qui concerne les collections, le retour d'une collection vide est de loin supérieur au retour de null, car l'attente de l'appelant de la méthode est qu'un nombre variable de résultats puisse être produit, donc une collection vide représente un conceptuellement valide résultat et est symétrique avec le concept de l'ensemble vide en mathématiques.

Cependant, les exceptions ont leur place surtout lorsqu'une méthode retourne void ou unit.

4
Aluan Haddad

Il est extrêmement important de faire la distinction entre les méthodes susceptibles de rencontrer des conditions d'erreur et celles qui ne le sont pas. Il est également tout aussi important d'avoir une compréhension claire du contrat pour cette méthode.

Si la méthode est celle qui rencontre régulièrement un échec (comme, par exemple, la récupération de données dans une base de données), alors elle a besoin d'un moyen de retourner le succès/l'échec ainsi que d'un moyen de retourner les données. Ma préférence est généralement de retourner true/false pour l'opération, d'utiliser un appel séparé pour récupérer les données et/ou l'état.

Si la méthode ne devrait pas rencontrer d'échec (comme le retour d'un élément d'une collection par index), elle doit jamais retourner null. Il doit renvoyer un élément valide, lever une exception ou affirmer. Les appelants doivent jamais vérifier le résultat du retour, car il sera toujours valide.

Il peut être pratique de renvoyer une valeur par défaut plutôt que d'affirmer, mais cela doit être reflété dans le contrat de méthode, et il peut y avoir deux méthodes (GetXxx et GetXxxOrDefault).

La vérification généralisée des valeurs de retour par rapport à null est souvent symptomatique de problèmes plus profonds dans le code. Les conseils trouvés dans les livres de programmation plus récents sont judicieux.


En réponse aux commentaires, considérons le cas où une méthode est requise pour effectuer une recherche, un calcul, une analyse, etc. et l'échec est une occurrence de routine. Je n'aime pas les exceptions et le formulaire TryParse (), car ils interrompent le flux du code, tandis que le formulaire FindOrDefault () est un problème si la valeur par défaut est une valeur de retour légale. J'ai tendance à préférer cet emballage:

public static U SafeLookup<T, U>(this Dictionary<T, U> dict, T key, U missing = default(U)) {
  U ret;
  if (dict.TryGetValue(key, out ret)) return ret;
  return missing;
}

Une valeur est toujours renvoyée et la valeur à renvoyer en cas d'erreur peut éventuellement être spécifiée.

2
david.pfx

J'aime l'idée de lever une exception au lieu de renvoyer null ou une autre valeur d'erreur. Disons que je conçois une nouvelle classe qui analyse un XML d'un certain format. J'aimerais que ma classe ait un seul point d'entrée, c'est-à-dire un constructeur qui spécifie le chemin vers un fichier XML valide. En retour, j'obtiens un objet avec un tas de propriétés et de méthodes qui contient le contenu de mon fichier XML et me donne quelques informations à ce sujet.

Maintenant, par exemple, comment puis-je m'assurer que mon chemin d'accès au fichier XML était valide? Je peux avoir, disons, une fonction Load(string xmlPath) dans ma classe. Quel devrait être son type de retour? Doit-il retourner un bool ou int ou quoi? Et, le plus intéressant, comment puis-je vérifier cela? Idéalement, je devrais faire la fonction Loadprivate, et je me moque même qu'elle existe. Mais si je veux vérifier le résultat de Load, alors la fonction devrait être faite public. Ou, passez une variable bool loadSuccesful À mon constructeur et remplissez-la avec le résultat du chargement du fichier. Mais attendez, que se passe-t-il si je veux comparer mon fichier XML avec un XSD? Je devrai avoir une autre fonction, parseXmlSchema(string xmlPath) ou quelque chose comme ça. Que devrait-il retourner en cas d'échec? Et comment pouvez-vous obtenir les informations de votre classe tout en gardant une interface propre en même temps? D'autres validations pourraient également être nécessaires.

Bien que, si vous rendez ces types de méthodes privés et que vous leviez une exception en cas de problème (il n'y a vraiment aucun intérêt à analyser votre fichier XML s'il n'est pas valide), vous pouvez avoir une interface propre et utiliser votre classe dans un try/catch. Vous pouvez avoir votre constructeur de classe avec un seul paramètre string (ou deux, si vous comptez le schéma XSD). Si tout va bien dans l'instruction try, si rien ne va, aucune exception n'est levée, des chemins existent, etc. votre programme est content. Si vous êtes actuellement dans la couche intermédiaire de votre application et obtenez un FileNotFoundException de vos couches inférieures, vous pouvez rethrow cela et le transmettre à votre utilisateur (GUI) pour faire quelque chose. La couche GUI pourrait l'attraper à ce moment et alerter l'utilisateur, par exemple via un messageBox, et l'enregistrer en bonus. L'utilisateur essaie ensuite un autre fichier XML et continue l'histoire. Si vous interceptez toutes les exceptions possibles et affichez des informations utiles à l'utilisateur ou prenez les mesures appropriées en arrière-plan, il n'est pas nécessaire que votre application se fige (vous l'avez probablement vu), si c'est ce dont vous avez peur.

En prime, vous pouvez transmettre toutes sortes d'informations significatives au point où votre exception a été levée, comme les messages, les noms de paramètres, les numéros de ligne, le nom de la fonction, etc. Si vous documentez votre code (comme vous le devriez), documentez les exceptions qu'un classe/méthode peut lancer.

1
mihai