Ceci est un code C # valide
var bob = "abc" + null + null + null + "123"; // abc123
Ce n'est pas un code C # valide
var wtf = null.ToString(); // compiler error
Pourquoi la première déclaration est-elle valide?
La raison du premier travail:
De MSDN :
Dans les opérations de concaténation de chaînes, le compilateur C # traite une chaîne nulle de la même manière qu'une chaîne vide, mais il ne convertit pas la valeur de la chaîne nulle d'origine.
Plus d'informations sur + opérateur binaire :
L'opérateur binaire + effectue la concaténation de chaînes lorsqu'un ou les deux opérandes sont de type chaîne.
Si un opérande de concaténation de chaînes est nul, une chaîne vide est substituée. Sinon, tout argument non-chaîne est converti en sa représentation sous forme de chaîne en appelant la méthode virtuelle
ToString
héritée de l'objet type.Si
ToString
renvoienull
, une chaîne vide est remplacée.
La raison de l'erreur en seconde est:
null (référence C #) - Le mot clé null est un littéral qui représente une référence null, qui ne fait référence à aucun objet. null est la valeur par défaut des variables de type référence.
Parce que l'opérateur +
En C # se traduit en interne par String.Concat
, Qui est une méthode statique. Et cette méthode arrive à traiter null
comme une chaîne vide. Si vous regardez la source de String.Concat
Dans Reflector, vous le verrez:
// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array
(MSDN le mentionne également: http://msdn.Microsoft.com/en-us/library/k9c94ey1.aspx )
D'un autre côté, ToString()
est une méthode d'instance, que vous ne pouvez pas appeler sur null
(quel type doit être utilisé pour null
?).
premier échantillon sera traduit en:
var bob = String.Concat("abc123", null, null, null, "abs123");
La méthode Concat
vérifie l'entrée et traduit null comme une chaîne vide
Le deuxième échantillon sera traduit en:
var wtf = ((object)null).ToString();
Une exception de référence null
sera donc générée ici
La première partie de votre code est traitée comme ça dans String.Concat
,
qui est ce que le compilateur C # appelle lorsque vous ajoutez des chaînes. "abc" + null
Est traduit en String.Concat("abc", null)
,
et en interne, cette méthode remplace null
par String.Empty
. C'est pourquoi votre première partie de code ne lève aucune exception. c'est comme
var bob = "abc" + string.Empty + string.Empty + string.Empty + "123"; //abc123
Et dans la 2ème partie de votre code lève une exception car 'null' n'est pas un objet, le mot clé null est un littéral qui représente une référence null, celui qui ne fait référence à aucun objet. null est la valeur par défaut des variables de type référence.
Et 'ToString()
' est une méthode qui peut être appelée par une instance d'un objet mais pas par n'importe quel littéral.
Dans le cadre COM qui précédait .net, il était nécessaire que toute routine qui recevait une chaîne la libère lorsqu'elle en avait fini avec elle. Parce qu'il était très courant que des chaînes vides soient passées dans et hors des routines, et parce que la tentative de "libérer" un pointeur nul était définie comme une opération légitime de ne rien faire, Microsoft a décidé qu'un pointeur de chaîne nul représente une chaîne vide.
Pour permettre une certaine compatibilité avec COM, de nombreuses routines en .net interpréteront un objet nul comme une représentation légale comme une chaîne vide. Avec quelques légères modifications .net et ses langages (permettant notamment aux membres d'instance d'indiquer "ne pas invoquer comme virtuel"), Microsoft aurait pu faire en sorte que les objets null
de type déclaré String se comportent encore plus comme des chaînes vides. Si Microsoft avait fait cela, il aurait également dû faire Nullable<T>
fonctionne quelque peu différemment (afin de permettre à Nullable<String>
-- quelque chose qu'ils devraient avoir fait à mon humble avis de toute façon) et/ou définir un type NullableString
qui serait principalement interchangeable avec String
, mais qui ne considérerait pas un null
comme une chaîne vide valide.
En l'état, il existe certains contextes dans lesquels un null
sera considéré comme une chaîne vide légitime et d'autres dans lesquels il ne le sera pas. Pas une situation extrêmement utile, mais une situation dont les programmeurs devraient être conscients. En général, les expressions de la forme stringValue.someMember
échouera si stringValue
est null
, mais la plupart des méthodes de framework et des opérateurs qui acceptent des chaînes comme paramètres considéreront null
comme une chaîne vide.
'+'
Est un opérateur d'infixe. Comme tout opérateur, il appelle vraiment une méthode. Vous pourriez imaginer une version non infixe "wow".Plus(null) == "wow"
Le réalisateur a décidé quelque chose comme ça ...
class String
{
...
String Plus(ending)
{
if(ending == null) return this;
...
}
}
Alors .. votre exemple devient
var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123"); // abc123
ce qui est le même que
var bob = "abc".Plus("123"); // abc123
A aucun moment, null ne devient une chaîne. Donc null.ToString()
n'est pas différent de null.VoteMyAnswer()
. ;)
Quelqu'un a dit dans ce fil de discussion que vous ne pouvez pas faire une chaîne à partir de rien. (ce qui est une belle phrase à mon avis). Mais oui - vous pouvez :-), comme le montre l'exemple suivant:
var x = null + (string)null;
var wtf = x.ToString();
fonctionne bien et ne lève pas du tout une exception. La seule différence est que vous devez transtyper l'une des valeurs nulles dans une chaîne - si vous supprimez la conversion (chaîne), alors l'exemple compile toujours, mais lève une exception au moment de l'exécution: "Opérateur" + 'est ambigu sur les opérandes de type' <null> 'et' <null> '".
N.B. Dans l'exemple de code ci-dessus, la valeur de x n'est pas nulle comme vous pouvez vous y attendre, il s'agit en fait d'une chaîne vide après avoir casté l'un des opérandes dans une chaîne.
Un autre fait intéressant est qu'en C #/.NET la façon dont null
est traitée n'est pas toujours la même si vous considérez différents types de données. Par exemple:
int? x = 1; // string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());
En ce qui concerne 1ère ligne de l'extrait de code: si x
est une variable entière nullable (c'est-à-dire int?
) contenant la valeur 1
, vous obtenez le résultat <null>
retour. S'il s'agit d'une chaîne (comme indiqué dans le commentaire) avec la valeur "1"
, alors vous obtenez "1"
retour plutôt que <null>
.
N.B. Également intéressant: si vous utilisez var x = 1;
pour la première ligne, vous obtenez une erreur d'exécution. Pourquoi? Parce que l'affectation transformera la variable x
en type de données int
, qui n'est pas nullable. Le compilateur ne suppose pas int?
ici, et donc échoue dans la 2ème ligne où null
est ajouté.
Je suppose que c'est un littéral qui ne fait référence à aucun objet. ToString()
a besoin d'un object
.
L'ajout de null
à une chaîne est simplement ignoré. null
(dans votre deuxième exemple) n'est une instance d'aucun objet, il n'a donc même pas de méthode ToString()
. C'est juste un littéral.
Parce qu'il n'y a pas de différence entre string.Empty
et null
lorsque vous concattez des chaînes. Vous pouvez passer null dans string.Format
ainsi que. Mais vous essayez d'appeler une méthode sur null
, ce qui entraînerait toujours un NullReferenceException
et générerait donc une erreur de compilation.
Si pour une raison quelconque vous voulez vraiment le faire, vous pouvez écrire une méthode d'extension, qui vérifie null
puis retourne string.Empty
. Mais une extension comme celle-ci ne devrait être utilisée qu'en cas de nécessité absolue (à mon avis).
En général: il peut ou non valider l'acceptation de null comme paramètre selon la spécification, mais il n'est pas toujours valide d'appeler une méthode sur null.
C'est et d'autres sujets pourquoi les opérandes de l'opérateur + peuvent être nuls en cas de chaînes. C'est un peu VB chose (désolé les gars) pour rendre la vie des programmeurs plus facile, ou en supposant que le programmeur ne peut pas gérer les valeurs nulles. Je suis complètement en désaccord avec cette spécification. 'Inconnu' + 'tout' devrait être toujours 'inconnue'...