J'ai eu un petit débat avec un collègue récemment. Nous utilisons spécifiquement C #, mais cela pourrait s'appliquer à n'importe quel langage avec des types nullables. Disons par exemple que vous avez une valeur qui représente un maximum. Cependant, cette valeur maximale est facultative. Je soutiens qu'un nombre annulable serait préférable. Mon collègue est favorable à l'utilisation de zéro, citant un précédent. Certes, des choses comme les sockets réseau ont souvent utilisé zéro pour représenter un délai illimité. Si je devais écrire du code traitant des sockets aujourd'hui, j'utiliserais personnellement une valeur nullable, car je pense qu'elle représenterait mieux le fait qu'il n'y a AUCUN timeout.
Quelle représentation est la meilleure? Les deux nécessitent une condition vérifiant la valeur signifiant «aucun», mais je crois qu'un type nullable transmet l'intention un peu mieux.
la source
Réponses:
Considérer:
La langue,
Cadre,
Le contexte.
1. Langue
L'utilisation de ∞ peut être une solution pour un maximum.
JavaScript, par exemple, a une infinité. C # ne fait pas¹.
Ada, par exemple, a des plages. C # ne le fait pas.
En C #, il y en a
int.MaxValue
, mais vous ne pouvez pas l'utiliser dans votre cas.int.MaxValue
est l'entier maximum, 2.147.483.647. Si dans votre code, vous avez une valeur maximale de quelque chose, comme une pression maximale acceptée avant que quelque chose explose, l'utilisation de 2147483647 n'a aucun sens.2. Cadre
.NET Framework est plutôt incohérent sur ce point, et son utilisation des valeurs magiques peut être critiquée.
Par exemple,
"Hello".IndexOf("Z")
renvoie une valeur magique-1
. Cela facilite peut-être (le fait-il?) La manipulation du résultat:plutôt que d'utiliser une structure personnalisée:
mais n'est pas du tout intuitif. Pourquoi
-1
et non-123
? Un débutant peut également penser à tort que cela0
signifie aussi "Introuvable" ou simplement taper du texte(position >= 0)
.3. Contexte
Si votre code est lié à des délais d'expiration dans les sockets réseau, utiliser quelque chose qui a été utilisé par tout le monde pendant des décennies dans un souci de cohérence n'est pas une mauvaise idée . Surtout,
0
car un timeout est très clair: c'est une valeur qui ne peut pas être nulle. L'utilisation d'une classe personnalisée dans ce cas peut rendre les choses plus difficiles à comprendre:Duration
à 0 siIsTimeoutEnabled
c'est vrai?IsTimeoutEnabled
est faux, que se passe-t-il si je metsDuration
100?Cela peut conduire à de multiples erreurs. Imaginez le morceau de code suivant:
L'opération dure dix secondes. Pouvez-vous voir ce qui ne va pas avec ce code, sans lire la documentation de la
Timeout
classe?Conclusion
null
exprime bien l'idée que la valeur n'est pas là. Ce n'est pas fourni. Indisponible. Ce n'est ni un nombre, ni une chaîne vide / nulle ou quoi que ce soit. Ne l'utilisez pas pour des valeurs maximales ou minimales.int.MaxValue
est fortement liée à la langue elle-même. Ne pas utiliserint.MaxValue
pour une limite de vitesse maximale deVehicle
classe ou une vitesse maximale acceptable pour un avion, etc.Évitez les valeurs magiques comme
-1
dans votre code. Ils sont trompeurs et entraînent des erreurs de code.Créez votre propre classe qui serait plus simple, avec les valeurs minimum / maximum spécifiées. Par exemple,
VehicleSpeed
peut avoirVehicleSpeed.MaxValue
.Ne suivez aucune directive précédente et utilisez des valeurs magiques si c'est une convention générale depuis des décennies dans un domaine très spécifique, utilisé par la plupart des gens qui écrivent du code dans ce domaine.
N'oubliez pas de mélanger les approches. Par exemple:
¹ Vous pouvez créer votre propre type qui inclut l'infini. Ici, je parle
int
uniquement de type natif .la source
int
n'exprime pas suffisamment sur le type pour contraindre le problème, envisagez une nouvelle structure avec plus d'informations (des instances const de la structure qui représentent des valeurs magiques, par exemple, ou une énumération dessus pour indiquer). Ou envisagez la programmation de contrats ou d'autres solutions, mais je pense qu'une structure personnalisée est la plus simple.Null n'est pas mieux qu'un nombre magique.
L'important est de NOMMER les valeurs qui ont des effets magiques, si vous devez avoir de telles valeurs, et de vous assurer que les définitions de ces noms sont quelque part qui seront vues par quiconque se heurtera à la valeur magique et aux wtf.
la source
MAGIC_NUMBER
le code doit absolument toujours être évité autant que possible.null
est une expression d'intention beaucoup plus claire.la source
En C #, de nombreuses classes CLR ont un
Empty
membre statique :System.String.Empty
System.EventArgs.Empty
System.Guid.Empty
System.Drawing.Rectangle.Empty
System.Windows.Size.Empty
Cela vous évite d'avoir à vous rappeler s'il faut utiliser une valeur magique ou utiliser null pour construire un objet vide.
Mais que faire si vous avez affaire à un type de valeur simple comme un
int
? Dans ce cas, demandez-vous si vous êtes victime de l' obsession primitive . Il est tout à fait possible que votre propriété numérique apparemment simple bénéficie de sa propre classe ou structure, ce qui vous permettrait de spécifier leEmpty
membre et également d'ajouter d'autres comportements spécifiques à ce type de valeur.la source
Dans ce cas, la valeur nulle est un excellent moyen d'indiquer qu'il n'y a pas de maximum. Généralement, lorsque le cas spécial signifie que la valeur en question ne s'applique pas, que vous ne voulez tout simplement pas la fonctionnalité qu'il configure, null est une bonne indication de cela.
Un problème avec l'utilisation de null pour représenter des cas spéciaux est qu'il n'y a qu'une seule valeur null et qu'il peut y avoir plusieurs cas spéciaux. Dans ce cas, je passerais une énumération comme paramètre supplémentaire, ce qui peut indiquer un cas spécial, ou pour utiliser la valeur int normalement. (C'est essentiellement ce que Nullable <> fait pour vous, bien qu'il utilise un booléen au lieu d'une énumération et combine les paramètres en une seule structure.)
la source
Dans ce cas, je pense qu'un type nullable est parfaitement logique.
Null signifie absence de valeur. Il s'agit d'un concept nettement différent d'un nombre ayant une valeur de 0.
Si vous voulez dire "Si je ne vous donne pas de valeur, utilisez le maximum", alors passer null est la façon exacte d'exprimer cela.
la source
Null: valeur d'erreur commune, non spécifiée, nulle ou absence de valeur.
Zéro: une valeur réelle, mais pas nécessairement logique ou intuitive (dans ce contexte). Également une valeur commune à l'initialisation.
Dans le contexte de votre problème, la
timeoutInMilliseconds
propriété est facultative et il n'y a aucune mention que la surcharge de cette approche la disqualifierait en option.Conclusion: il existe des exceptions et les solutions varient selon les langues et les domaines; dans ce cas, je choisirais Null. Là où (je crois) certaines personnes se trompent, c'est quand elles ne séparent pas bien les données de l'interface. Ils s'attendent à ce que tout client lise la documentation (ou l'implémentation) pour déterminer comment ces valeurs spéciales doivent être utilisées / gérées - les cas spéciaux s'infiltrent dans le programme du client et cela peut être assez peu clair. En ajoutant une bonne couche d'abstraction, l'utilisation peut être beaucoup plus claire.
la source
Null est pire à utiliser que
MagicNumber
. Null représente mieux l'idée exprimée, mais il n'est pas cohérent sur toutes les plates-formes dans son comportement, en utilisantMagicNumber
toujours le même, ce qui est bénéfique.selon l'environnement / la langue utilisée, null pourrait
MagicNumber
se comporte toujours de la même manière.la source
Si vous oubliez de vérifier le nombre magique (cela se produira correctement), un nombre magique continuera pendant un petit moment avec des données absurdes. Il vaut mieux avoir un null qui provoque une exception dès que possible.
la source
Null n'est pas la seule alternative à un nombre magique.
Null est mal. Dans l'exemple ci-dessus, vous pourrez peut-être vous en sortir car le code serait évidemment capable de gérer un null. Mais en général, ce qui se passe lorsque vous commencez à transmettre des valeurs nulles, c'est que tôt ou tard, vous obtenez une exception de pointeur nul. Cela peut ne pas se produire lors de la première écriture du code, mais le code est conservé bien plus longtemps que la première version. Il est souvent maintenu par des personnes qui ne connaissent pas autant le système que les développeurs d'origine.
Scala (par exemple) a une belle alternative dans la classe Option. La classe Option a l'une des deux valeurs, Some - qui encapsule la valeur que vous voulez vraiment et None - qui n'a pas de valeur.
Cela rend évident pour tout développeur qu'il peut ne pas y avoir de valeur et que vous aviez un meilleur code pour cela. Eh bien, cela devrait être évident de toute façon.
Et tous les nombres magiques ne sont pas un problème. Selon le contexte 0, 1, 1024, etc., tout peut être évident. 347? Ouais, celui que tu devrais éviter. :-)
la source