C'est assez ennuyant de tester toutes mes chaînes pour null
avant de pouvoir appliquer en toute sécurité des méthodes comme ToUpper()
, StartWith()
etc ...
Si la valeur par défaut de string
était la chaîne vide, je n'aurais pas à effectuer de test et je le sentirais plus cohérent avec les autres types de valeur tels que int
ou double
par exemple . De plus, Nullable<String>
serait logique.
Alors pourquoi les concepteurs de C # ont-ils choisi d'utiliser null
comme valeur par défaut des chaînes?
Remarque: Ceci concerne cette question , mais est plus centré sur le pourquoi que sur ce qu’il faut en faire.
Pourquoi est la valeur par défaut du type de chaîne null au lieu d’un vide chaîne?
Parce que string
est un type reference et que la valeur par défaut pour tous les types de référence est null
.
C'est assez ennuyant de tester toutes mes chaînes pour null avant que je puisse appliquer en toute sécurité des méthodes comme ToUpper (), StartWith () etc ...
Cela correspond au comportement des types de référence. Avant d'appeler leurs membres d'instance, il convient de cocher une référence null.
Si la valeur par défaut de chaîne était la chaîne vide, je n'aurais pas pour tester, et je le sentirais pour être plus compatible avec l'autre types de valeur comme int ou double par exemple.
Assigner la valeur par défaut à un type de référence spécifique autre que null
le rendrait inconsistent.
De plus,
Nullable<String>
aurait du sens.
Nullable<T>
fonctionne avec les types de valeur. Il convient de noter le fait que Nullable
n'a pas été introduit sur la plate-forme .NET d'origine il y aurait donc eu beaucoup de codes cassés s'ils avaient changé cette règle. (Courtoisie @jcolebrand )
Habib a raison - parce que string
est un type de référence.
Mais plus important encore, vous n'avez pas à vérifier null
à chaque fois que vous l'utilisez. Cependant, vous devriez probablement lancer une ArgumentNullException
si quelqu'un passe à votre fonction une référence null
.
Voici la chose - le framework jetterait une NullReferenceException
de toute façon si vous essayiez d'appeler .ToUpper()
sur une chaîne. N'oubliez pas que ce cas peut toujours se produire même si vous testez vos arguments pour null
, étant donné que toute propriété ou méthode sur les objets transmis à votre fonction en tant que paramètres peut être évaluée à null
.
Cela étant dit, la vérification des chaînes vides ou des valeurs NULL est une chose courante à faire. Ils fournissent donc String.IsNullOrEmpty()
et String.IsNullOrWhiteSpace()
uniquement à cette fin.
Vous pouvez écrire une méthode extension (pour ce que cela vaut):
public static string EmptyNull(this string str)
{
return str ?? "";
}
Maintenant cela fonctionne en toute sécurité:
string str = null;
string upper = str.EmptyNull().ToUpper();
Les chaînes vides et les valeurs nulles sont fondamentalement différentes. Un null est l'absence d'une valeur et une chaîne vide est une valeur vide.
Le langage de programmation faisant des hypothèses sur la "valeur" d'une variable, dans ce cas une chaîne vide, équivaudra à initier la chaîne avec toute autre valeur ne causant pas de problème de référence null.
De même, si vous transmettez le handle de cette variable chaîne à d'autres parties de l'application, ce code n'aura aucun moyen de valider si vous avez intentionnellement passé une valeur vide ou si vous avez oublié de renseigner la valeur de cette variable.
Cela peut également poser problème lorsque la chaîne est une valeur renvoyée par une fonction. Puisque chaîne est un type de référence et qu'elle peut techniquement avoir la valeur null et que les deux sont vides, la fonction peut également renvoyer techniquement un null ou un vide (rien ne l'empêche de le faire). Maintenant, étant donné qu’il existe 2 notions de "l’absence de valeur", c’est-à-dire une chaîne vide et une valeur nulle, tout le code qui consomme cette fonction devra effectuer 2 vérifications. Un pour vide et l'autre pour null.
En bref, il est toujours bon d’avoir une seule représentation pour un seul État. Pour une discussion plus large sur les valeurs vides et nulles, voir les liens ci-dessous.
https://softwareengineering.stackexchange.com/questions/32578/sql-empty-string-vs-null-value
Vous pouvez également (à partir de VS2015 et du nouveau compilateur) utiliser les éléments suivants:
string myString = null;
string result = myString?.ToUpper();
Le résultat de la chaîne sera nul.
Parce qu'une variable de chaîne est un reference, pas un instance.
L'initialiser à Vide par défaut aurait été possible, mais cela aurait introduit beaucoup d'incohérences dans tous les domaines.
La raison fondamentale/problème est que les concepteurs de la spécification CLS (qui définit la manière dont les langages interagissent avec .net) ne définissent pas de moyen permettant aux membres de la classe de spécifier qu'ils doivent être appelés directement, plutôt que via callvirt
, sans que l'appelant ne une vérification de référence nulle; cela ne permettait pas non plus de définir des structures qui ne seraient pas soumises à la boxe "normale".
Si la spécification CLS avait défini un tel moyen, il serait alors possible pour .net de suivre systématiquement le fil établi par le modèle COM (Common Object Model), selon lequel une référence de chaîne nulle était considérée comme sémantiquement équivalente à une chaîne vide. types de classe immuables définis par l'utilisateur qui sont supposés avoir une sémantique de valeur pour définir également des valeurs par défaut. Essentiellement, ce qui arriverait serait pour chaque membre de String
, par ex. Length
doit être écrit comme quelque chose comme [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }
. Cette approche aurait offert une très bonne sémantique pour des choses qui devraient se comporter comme des valeurs, mais en raison de problèmes d’implémentation, elles doivent être stockées sur le tas. La plus grande difficulté de cette approche est que la sémantique de la conversion entre de tels types et Object
peut devenir un peu trouble.
Une autre approche aurait été de permettre la définition de types de structure spéciaux qui n’héritaient pas de Object
, mais qui comportaient plutôt des opérations de boxing et unboxing personnalisées (qui seraient converties vers/depuis un autre type de classe). Avec une telle approche, il y aurait une classe NullableString
qui se comporterait comme une chaîne maintenant, et une structure personnalisée type String
, qui contiendrait un seul champ privé Value
de type String
. Tenter de convertir une variable String
en NullableString
ou Object
renverrait Value
si non-nul ou String.Empty
si nul. En essayant de transtyper en String
, une référence non nulle à une instance NullableString
stockerait la référence dans Value
(peut-être stocker null si la longueur était égale à zéro); lancer toute autre référence jetterait une exception.
Même si les chaînes doivent être stockées sur le tas, il n'y a théoriquement aucune raison pour laquelle elles ne devraient pas se comporter comme les types de valeur qui ont une valeur par défaut non nulle. Le fait de les stocker en tant que structure "normale" contenant une référence aurait été efficace pour le code les utilisant comme type "chaîne", mais aurait ajouté une couche supplémentaire d'indirection et d'inefficacité lors de la conversion en "objet". Bien que je ne prévoie pas .net d'ajouter l'une ou l'autre des fonctionnalités ci-dessus à cette date tardive, les concepteurs de futurs frameworks pourraient envisager de les inclure.
Pourquoi les concepteurs de C # ont choisi d’utiliser null comme valeur par défaut de des cordes?
Comme les chaînes sont reference types, les types de référence sont par défaut. La valeur est null
. Les variables des types de référence stockent des références aux données réelles.
Utilisons default
mot-clé pour ce cas;
string str = default(string);
str
est une string
, donc c'est un type référence, donc la valeur par défaut est null
.
int str = (default)(int);
str
est une int
, donc c'est un type value, donc la valeur par défaut est zero
.
Si la valeur par défaut de
string
était la chaîne vide, je n'aurais pas à tester
Faux! Changer la valeur par défaut ne change pas le fait qu'il s'agisse d'un type de référence et que quelqu'un peut toujours explicitement set la référence à null
.
De plus,
Nullable<String>
aurait du sens.
Vrai point. Il serait plus logique de ne pas autoriser null
pour les types de référence, mais d'exiger Nullable<TheRefType>
pour cette fonctionnalité.
Alors pourquoi les concepteurs de C # ont-ils choisi d'utiliser
null
comme valeur par défaut des chaînes?
Cohérence avec d'autres types de référence. Maintenant, pourquoi autoriser null
dans les types de référence? Probablement pour que cela ressemble à C, même s’il s’agit là d’une décision de conception douteuse dans un langage qui fournit également Nullable
.
Peut-être que si vous utilisiez l'opérateur ??
lors de l'affectation de votre variable chaîne, cela pourrait vous aider.
string str = SomeMethodThatReturnsaString() ?? "";
// if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.
Une chaîne est un objet immuable, ce qui signifie que, lorsqu'une valeur est donnée, l'ancienne valeur n'est pas effacée de la mémoire, mais reste à l'ancien emplacement et la nouvelle valeur est placée dans un nouvel emplacement. Donc, si la valeur par défaut de String a
était String.Empty
, le bloc String.Empty
serait perdu en mémoire lorsqu'il aurait reçu sa première valeur.
Bien que cela semble minuscule, cela pourrait devenir un problème lors de l’initialisation d’un grand tableau de chaînes avec les valeurs par défaut de String.Empty
. Bien sûr, vous pouvez toujours utiliser la classe mutable StringBuilder
si cela devait poser un problème.
Puisque string est un type de référence et que la valeur par défaut du type de référence est null.
Peut-être que le mot clé string
vous a dérouté, car il ressemble exactement à n'importe quelle autre déclaration value type, mais il s'agit en fait d'un alias de System.String
, comme expliqué dans cette question .
De plus, la couleur bleu foncé dans Visual Studio et la première lettre minuscule peuvent donner à penser qu'il s'agit d'une struct
.
Les types nullables ne sont pas entrés avant la version 2.0.
Si des types nullables avaient été créés au début du langage, alors string aurait été non-nullable et string? aurait été nullable. Mais ils ne pouvaient pas faire cela à cause de la compatibilité ascendante.
Beaucoup de gens parlent de type ref ou non, mais string est une classe hors de l'ordinaire et des solutions auraient été trouvées pour le rendre possible.