Utiliser correctement null
Il y a différentes façons d'utiliser null
. La manière la plus courante et sémantiquement correcte consiste à l'utiliser lorsque vous pouvez avoir ou non une valeur unique . Dans ce cas, une valeur est égale null
ou significative (par exemple, un enregistrement de base de données).
Dans ces situations, vous l'utilisez principalement comme ceci (en pseudo-code):
if (value is null) {
doSomethingAboutIt();
return;
}
doSomethingUseful(value);
Problème
Et c'est un très gros problème. Le problème est qu’au moment où vous appelez, doSomethingUseful
la valeur n’a peut-être pas été vérifiée null
! Si ce n'était pas le cas, le programme va probablement planter. Et l'utilisateur peut même ne pas voir de bons messages d'erreur, laissant quelque chose comme "une erreur horrible: valeur recherchée mais nulle"! " (après la mise à jour: bien qu'il puisse y avoir encore moins d'erreur informative comme Segmentation fault. Core dumped.
, ou pire encore, aucune erreur et une manipulation incorrecte sur null dans certains cas)
Oublier d'écrire des chèques null
et de gérer des null
situations est un bogue extrêmement courant . Voilà pourquoi Tony Hoare qui a inventé null
lors d' une conférence de logiciel appelé QCon Londres en 2009 qu'en 1965 , il a fait l'erreur d'un milliard de dollars:
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Erreur-Tony-Hoare
Éviter le problème
Certaines technologies et langues rendent la vérification null
impossible à oublier de différentes manières, réduisant ainsi le nombre de bugs.
Par exemple, Haskell a la Maybe
monade au lieu de NULL. Supposons qu'il s'agisse d' DatabaseRecord
un type défini par l'utilisateur. Dans Haskell, une valeur de type Maybe DatabaseRecord
peut être égale Just <somevalue>
ou égale à Nothing
. Vous pouvez ensuite l'utiliser de différentes manières, mais vous ne pouvez pas appliquer une opération Nothing
sans le savoir, peu importe la façon dont vous l'utilisez .
Par exemple, cette fonction appelée zeroAsDefault
renvoie x
pour Just x
et 0
pour Nothing
:
zeroAsDefault :: Maybe Int -> Int
zeroAsDefault mx = case mx of
Nothing -> 0
Just x -> x
Christian Hackl dit que C ++ 17 et Scala ont leurs propres méthodes. Donc, vous voudrez peut-être essayer de savoir si votre langue a quelque chose comme ça et l'utiliser.
Les null sont encore largement utilisés
Si vous n'avez rien de mieux à utiliser, alors null
c'est bien. Continuez juste à le surveiller. Les déclarations de type dans les fonctions vous aideront quand même un peu.
En outre, cela peut sembler peu progressif, mais vous devriez vérifier si vos collègues veulent utiliser null
quelque chose d’autre. Ils peuvent être conservateurs et ne pas vouloir utiliser de nouvelles structures de données pour certaines raisons. Par exemple, supporter les anciennes versions d'une langue. Ces éléments doivent être déclarés dans les normes de codage du projet et dûment discutés avec l'équipe.
Sur votre proposition
Vous suggérez d'utiliser un champ booléen séparé. Mais vous devez quand même le vérifier et vous pouvez toujours oublier de le vérifier. Donc, il n'y a rien gagné ici. Si vous pouvez même oublier autre chose, comme mettre à jour les deux valeurs à chaque fois, c'est encore pire. Si le problème de l’oubli de la vérification null
n’est pas résolu, cela ne sert à rien. Éviter null
est difficile et vous ne devriez pas le faire de manière à aggraver les choses.
Comment ne pas utiliser null
Enfin, il existe des moyens courants d'utiliser de null
manière incorrecte. L’un de ces moyens consiste à l’utiliser à la place de structures de données vides telles que des tableaux et des chaînes. Un tableau vide est un tableau approprié comme un autre! Il est presque toujours important et utile que les structures de données, qui peuvent contenir plusieurs valeurs, puissent être vides, c'est-à-dire qu'elles ont une longueur égale à 0.
Du point de vue de l'algèbre, une chaîne vide pour les chaînes ressemble beaucoup à 0 pour les nombres, c'est-à-dire l'identité:
a+0=a
concat(str, '')=str
Une chaîne vide permet aux chaînes de devenir en général un monoïde:
https://en.wikipedia.org/wiki/Monoid
Si vous ne l'obtenez pas, ce n'est pas si important pour vous.
Voyons maintenant pourquoi il est important de programmer avec cet exemple:
for (element in array) {
doSomething(element);
}
Si nous passons ici un tableau vide, le code fonctionnera correctement. Cela ne fera que rien. Cependant, si nous passons un null
ici, nous aurons probablement un crash avec une erreur du type "impossible de parcourir en boucle null, désolé". Nous pourrions l'envelopper if
mais c'est moins propre et encore une fois, vous pourriez oublier de vérifier
Comment gérer null
Ce qui doSomethingAboutIt()
devrait être fait et surtout s’il faut lever une exception est une autre question compliquée. En bref, cela dépend de la question de savoir si null
était une valeur d'entrée acceptable pour une tâche donnée et ce qui est attendu en réponse. Les exceptions concernent des événements inattendus. Je n'irai pas plus loin dans ce sujet. Cette réponse est déjà très longue.
std::optional
ouOption
. Dans d’autres langues, vous devrez peut-être créer vous-même un mécanisme approprié, ou bien vous pourrez recourir ànull
quelque chose de similaire, car il est plus idiomatique.