Je ne comprends pas très bien le dénigrement cohérent des références nulles par certains utilisateurs du langage de programmation. Qu'est-ce qui est si mauvais avec eux? Si je demande un accès en lecture à un fichier qui n'existe pas, je suis parfaitement heureux d'obtenir une exception ou une référence nulle et pourtant les exceptions sont considérées comme bonnes mais les références nulles sont considérées comme mauvaises. Quel est le raisonnement derrière cela?
programming-languages
exceptions
null
davidk01
la source
la source
Réponses:
Les références nulles ne sont pas "évitées" plus que les exceptions, du moins par quiconque que j'ai jamais connu ou lu. Je pense que vous comprenez mal la sagesse conventionnelle.
Ce qui est mauvais, c'est une tentative d' accéder à une référence nulle (ou de déréférencer un pointeur nul, etc.). C'est mauvais car cela indique toujours un bug; vous ne feriez jamais quelque chose comme ça sur le but, et si vous êtes en train de faire exprès, alors que est encore pire, parce que cela rend impossible de distinguer le comportement attendu d' un comportement buggy.
Il y a certains groupes marginaux qui détestent vraiment le concept de nullité pour une raison quelconque, mais comme Ed le fait remarquer , si vous n'en avez pas
null
ounil
alors vous devrez simplement le remplacer par quelque chose d'autre, ce qui pourrait conduire à quelque chose pire qu'un crash (comme la corruption de données).De nombreux cadres, en fait, embrassent les deux concepts; par exemple, dans .NET, un modèle fréquent que vous verrez est une paire de méthodes, une préfixée par le mot
Try
(commeTryGetValue
). Dans leTry
cas, la référence est définie sur sa valeur par défaut (généralementnull
), et dans l'autre cas, une exception est levée. Il n'y a rien de mal à l'une ou l'autre approche; les deux sont fréquemment utilisés dans les environnements qui les prennent en charge.Tout dépend vraiment de la sémantique. Si
null
est une valeur de retour valide, comme dans le cas général de la recherche d'une collection, alors retourneznull
. D'un autre côté, s'il ne s'agit pas d' une valeur de retour valide - par exemple, la recherche d'un enregistrement à l'aide d'une clé primaire provenant de votre propre base de données - alors le retournull
serait une mauvaise idée car l'appelant ne s'y attendrait pas et probablement ne le vérifiera pas.Il est vraiment très simple de déterminer la sémantique à utiliser: est -il logique que le résultat d'une fonction ne soit pas défini? Si c'est le cas, vous pouvez renvoyer une référence nulle. Sinon, jetez une exception.
la source
null
, donc vraiment, Haskell est assez hors de propos ici.null
a si bien duré si longtemps, et la majorité des gens qui disent le contraire semblent être des universitaires avec peu d'expérience dans la conception d'applications du monde réel avec des contraintes telles que des exigences incomplètes ou une cohérence éventuelle.La grande différence est que si vous omettez le code pour gérer les valeurs NULL, votre code continuera probablement à se bloquer à un stade ultérieur avec un message d'erreur non lié, où, comme pour les exceptions, l'exception serait levée au point d'échec initial (ouverture un fichier à lire dans votre exemple).
la source
Parce que les valeurs nulles ne sont pas une partie nécessaire d'un langage de programmation et sont une source cohérente de bogues. Comme vous le dites, l'ouverture d'un fichier peut entraîner un échec, qui peut être communiqué en retour sous la forme d'une valeur de retour nulle ou via une exception. Si les valeurs nulles n'étaient pas autorisées, il existe un moyen unique et cohérent de communiquer l'échec.
En outre, ce n'est pas le problème le plus courant avec les valeurs NULL. La plupart des gens se souviennent de vérifier null après avoir appelé une fonction qui peut la renvoyer. Le problème surgit beaucoup plus dans votre propre conception en permettant aux variables d'être nulles à différents moments de l'exécution de votre programme. Vous pouvez concevoir votre code de telle sorte que les valeurs nulles ne soient jamais autorisées, mais si null n'était pas autorisé au niveau de la langue, rien de tout cela ne serait nécessaire.
Cependant, dans la pratique, vous auriez encore besoin d'un moyen de signifier si une variable est ou non initialisée. Vous auriez alors une forme de bogue dans laquelle votre programme ne se bloque pas, mais continue à la place en utilisant une valeur par défaut éventuellement invalide. Honnêtement, je ne sais pas ce qui est mieux. Pour mon argent, j'aime planter tôt et souvent.
la source
null
une valeur de retour: les informations que vous avez demandées n'existent pas. Il n'y a aucune différence. Dans les deux cas, l'appelant doit valider la valeur de retour s'il a réellement besoin d'utiliser ces informations dans un but particulier. Qu'il s'agisse de valider unnull
, un objet nul ou une monade ne fait aucune différence pratique.Tony Hoare, qui a créé l'idée d'une référence nulle en premier lieu, l'appelle son erreur d'un million de dollars .
Le problème ne concerne pas les références nulles en soi, mais le manque de vérification de type appropriée dans la plupart des langues sûres (sinon) de type.
Ce manque de prise en charge, de la langue, signifie que les bogues "null-bugs" peuvent se cacher dans le programme pendant une longue période avant d'être détectés. Telle est la nature des bugs, bien sûr, mais les "null-bugs" sont désormais connus pour être évitables.
Ce problème est particulièrement présent en C ou C ++ (par exemple) à cause de l'erreur "dure" qu'il provoque (un crash du programme, immédiat, sans récupération élégante).
Dans d'autres langues, il y a toujours la question de savoir comment les gérer.
En Java ou C #, vous obtiendriez une exception si vous tentez d'appeler une méthode sur une référence nulle, et cela pourrait être correct. Et donc la plupart des programmeurs Java ou C # sont habitués à cela et ne comprennent pas pourquoi on voudrait faire autrement (et rire de C ++).
Dans Haskell, vous devez explicitement fournir une action pour le cas nul, et donc les programmeurs Haskell se moquent de leurs collègues, car ils l'ont bien compris (non?).
C'est, en fait, l'ancien débat sur les codes d'erreur / exceptions, mais cette fois avec une valeur sentinelle au lieu d'un code d'erreur.
Comme toujours, celui qui est le plus approprié dépend vraiment de la situation et de la sémantique que vous recherchez.
la source
null
est vraiment mauvais, car il subvertit le système de type . (Certes, l'alternative a un inconvénient dans sa verbosité, au moins dans certaines langues.)Vous ne pouvez pas joindre un message d'erreur lisible par un humain à un pointeur nul.
(Vous pouvez cependant laisser un message d'erreur dans un fichier journal.)
Dans certaines langues / environnements qui permettent l' arithmétique des pointeurs, si l' un des arguments de pointeur est nul et il est permis dans le calcul, le résultat serait invalide , non nul pointeur. (*) Plus de pouvoir pour vous.
(*) Cela se produit souvent dans la programmation COM , où si vous essayez d'appeler dans une méthode d'interface mais que le pointeur d'interface est nul, cela entraînerait un appel à une adresse invalide qui est presque, mais pas tout à fait, exactement différente de zéro.
la source
Renvoyer NULL (ou zéro numérique ou booléen faux) pour signaler une erreur est incorrect à la fois techniquement et conceptuellement.
Techniquement, vous obligez le programmeur à vérifier immédiatement la valeur de retour , au point exact où elle est retournée. Si vous ouvrez vingt fichiers d'affilée et que la signalisation d'erreur se fait en retournant NULL, le code consommateur doit vérifier chaque fichier lu individuellement et sortir de toutes les boucles et constructions similaires. Ceci est une recette parfaite pour le code encombré. Si, toutefois, vous choisissez de signaler l'erreur en lançant une exception, le code consommateur peut choisir de gérer l'exception immédiatement ou de la laisser remonter autant de niveaux que nécessaire, même entre les appels de fonction. Cela rend le code beaucoup plus propre.
Conceptuellement, si vous ouvrez un fichier et que quelque chose ne va pas, le retour d'une valeur (même NULL) est incorrect. Vous n'avez rien à retourner, car votre opération n'est pas terminée. Renvoyer NULL est l'équivalent conceptuel de "J'ai réussi à lire le fichier, et voici ce qu'il contient - rien". Si c'est ce que vous voulez exprimer (c'est-à-dire si NULL a un sens comme résultat réel pour l'opération en question), alors renvoyez NULL, mais si vous voulez signaler une erreur, utilisez des exceptions.
Historiquement, des erreurs ont été signalées de cette façon car les langages de programmation comme C n'ont pas de gestion des exceptions intégrée dans le langage, et la manière recommandée (en utilisant de longs sauts) est un peu velue et un peu contre-intuitive.
Il y a aussi un côté maintenance au problème: à quelques exceptions près, vous devez écrire du code supplémentaire pour gérer l'échec; si vous ne le faites pas, le programme échouera tôt et durement (ce qui est bien). Si vous retournez NULL pour signaler des erreurs, le comportement par défaut du programme est d'ignorer l'erreur et de continuer jusqu'à ce qu'il entraîne d'autres problèmes sur la route - données corrompues, segfaults, NullReferenceExceptions, selon la langue. Pour signaler l'erreur tôt et fort, vous devez écrire du code supplémentaire et deviner quoi: c'est la partie qui est laissée de côté lorsque vous êtes dans un délai serré.
la source
Comme déjà souligné, de nombreuses langues ne convertissent pas la déréférence d'un pointeur nul en exception capturable. Faire cela est une astuce relativement moderne. Lorsque le problème du pointeur nul a été reconnu pour la première fois, aucune exception n'avait encore été inventée.
Si vous autorisez les pointeurs nuls comme cas valide, il s'agit d'un cas spécial. Vous avez besoin d'une logique de traitement des cas spéciaux, souvent dans de nombreux endroits différents. C'est une complexité supplémentaire.
Qu'il s'agisse de pointeurs potentiellement nuls ou non, si vous n'utilisez pas de levées d'exceptions pour gérer les cas exceptionnels, vous devez gérer ces cas exceptionnels d'une autre manière. En règle générale, chaque appel de fonction doit faire l'objet de vérifications pour ces cas exceptionnels, soit pour empêcher l'appel de fonction d'être appelé de manière inappropriée, soit pour détecter le cas d'échec lorsque la fonction se termine. C'est une complexité supplémentaire qui peut être évitée en utilisant des exceptions.
Plus de complexité signifie généralement plus d'erreurs.
Les alternatives à l'utilisation de pointeurs nuls dans les structures de données (par exemple pour marquer le début / la fin d'une liste chaînée) incluent l'utilisation d'éléments sentinelles. Ceux-ci peuvent donner la même fonctionnalité avec beaucoup moins de complexité. Cependant, il peut y avoir d'autres façons de gérer la complexité. Une façon consiste à encapsuler le pointeur potentiellement nul dans une classe de pointeur intelligent afin que les vérifications nulles ne soient nécessaires qu'en un seul endroit.
Que faire du pointeur nul lorsqu'il est détecté? Si vous ne pouvez pas intégrer la gestion des cas exceptionnels, vous pouvez toujours lever une exception et déléguer efficacement cette gestion des cas spéciaux à l'appelant. Et c'est exactement ce que font certaines langues par défaut lorsque vous déréférencez un pointeur nul.
la source
Spécifique au C ++, mais il y a des références nulles car la sémantique nulle en C ++ est associée aux types de pointeurs. Il est tout à fait raisonnable qu'une fonction d'ouverture de fichier échoue et renvoie un pointeur nul; en fait, la
fopen()
fonction fait exactement cela.la source
Cela dépend de la langue.
Par exemple, Objective-C vous permet d'envoyer un message à un objet null (nil) sans problème. Un appel à zéro renvoie également zéro et est considéré comme une fonction de langue.
Personnellement, j'aime ça, car vous pouvez compter sur ce comportement et éviter toutes ces
if(obj == null)
constructions imbriquées alambiquées .Par exemple:
Peut être raccourci à:
En bref, cela rend votre code plus lisible.
la source
Je ne me soucie pas de savoir si vous retournez une valeur nulle ou lève une exception, tant que vous la documentez.
la source
GetAddressMaybe
ouGetSpouseNameOrNull
.Une référence Null se produit généralement parce que quelque chose dans la logique du programme a été manqué, c'est-à-dire: vous êtes arrivé à une ligne de code sans passer par la configuration requise pour ce bloc de code.
D'un autre côté, si vous lancez une exception pour quelque chose, cela signifie que vous avez reconnu qu'une situation particulière pouvait se produire dans le fonctionnement normal du programme et qu'elle était en cours de traitement.
la source
Les références nulles sont souvent très utiles: par exemple, un élément dans une liste chaînée peut avoir un successeur ou aucun successeur. L'utilisation
null
pour «aucun successeur» est parfaitement naturelle. Ou, une personne peut avoir un conjoint ou non - utilisernull
pour «personne n'a pas de conjoint» est parfaitement naturel, beaucoup plus naturel que d'avoir une valeur spéciale «n'a pas de conjoint» à laquelle lePerson.Spouse
membre pourrait se référer.Mais: de nombreuses valeurs ne sont pas facultatives. Dans un programme OOP typique, je dirais que plus de la moitié des références ne peuvent pas être
null
après l'initialisation, sinon le programme échouera. Sinon, le code devrait être criblé deif (x != null)
chèques. Alors, pourquoi chaque référence devrait-elle être annulable par défaut? Cela devrait vraiment être l'inverse: les variables doivent être non nulles par défaut, et vous devez explicitement dire "oh, et cette valeur peut l'êtrenull
aussi".la source
Votre question est formulée de manière confuse. Voulez-vous dire une exception de référence nulle (qui est en fait causée par une tentative de déréférence null)? La raison évidente de ne pas vouloir ce type d'exception est qu'elle ne vous donne aucune information sur ce qui n'a pas fonctionné, ni même quand - la valeur aurait pu être définie sur null à tout moment du programme. Vous écrivez "Si je demande un accès en lecture à un fichier qui n'existe pas, je suis parfaitement heureux d'obtenir une exception ou une référence nulle" - mais vous ne devriez pas être parfaitement heureux d'obtenir quelque chose qui ne donne aucune indication sur la cause . Nulle part dans la chaîne "une tentative a été faite de déréférencer null" est-il fait mention de lecture ou de fichiers inexistants. Peut-être voulez-vous dire que vous seriez parfaitement heureux d'obtenir null comme valeur de retour d'un appel de lecture - c'est une question très différente, mais cela ne vous donne toujours aucune information sur les raisons de l'échec de la lecture; il'
la source