Pourquoi l'incrémentation de Nullable <int> ne lève-t-elle pas une exception?

98

Pourriez-vous s'il vous plaît expliquer, pourquoi Console.WriteLine écrit-il une ligne vide ( Console.WriteLine(null)me donne une erreur de compilation) et pourquoi il n'y a pas NullReferenceException (même a+=1ne devrait pas le lever)?

int? a = null;
a++; // Why there is not NullReferenceException? 
Console.WriteLine(a); // Empty line
Maxim Joukov
la source
Tous les trois opérateurs ++, +=et +ont soulevé des variantes. Par conséquent, les déclarations a++;, a += 1;et a = a + 1;sont toutes autorisées. Chaque produit null(aucune exception lancée) aest initialement null.
Jeppe Stig Nielsen
5
NullReferenceException? mais ce int?n'est pas un Reference, c'est juste un intqui peut prendre de la nullvaleur
Khaled.K

Réponses:

124

Vous observez les effets d'un opérateur soulevé .

De la section 7.3.7 de la spécification C # 5:

Les opérateurs levés permettent aux opérateurs prédéfinis et définis par l'utilisateur qui opèrent sur des types de valeur non Nullable d'être également utilisés avec des formes Nullable de ces types. Les opérateurs levés sont construits à partir d'opérateurs prédéfinis et définis par l'utilisateur qui répondent à certaines exigences, comme décrit ci-dessous:

  • Pour les opérateurs unaires, + ++ - -- ! ~ une forme élevée d'opérateur existe si l'opérande et les types de résultat sont tous deux des types valeur non Nullable. La forme levée est construite en ajoutant un seul ?modificateur aux types d'opérande et de résultat. L'opérateur lifted produit une valeur nulle si l'opérande est nul. Sinon, l'opérateur lifted déballe l'opérande, applique l'opérateur sous-jacent et encapsule le résultat.

Donc, fondamentalement, a++dans ce cas est une expression avec un résultat de null(en tant que int?) et la variable est laissée intacte.

Quand vous appelez

Console.WriteLine(a);

qui est encadré dans object, qui le convertit en une référence nulle, qui est imprimée sous forme de ligne vide.

Jon Skeet
la source
3
Existe-t-il un moyen d'imprimer "null" sans utiliser Console.WriteLine(a == null ? "null" : a)?
Cole Johnson
5
@Cole Johnson: Console.WriteLine (un ?? "nul"); :)
David Chappelle
2
@DavidChappelle Quand aest de type int?, je dirais qu'il a ?? "null"ne trouve aucun type commun pour les deux opérandes. Vous avez besoin d'un indice qui objectest le type courant. Alors lancez l'un ou l'autre des opérandes en cela. Le asera en boîte. Par exemple ((object)a) ?? "null"ou a ?? (object)"null".
Jeppe Stig Nielsen
super. pouvez-vous créer un lien vers les spécifications? pouvons-nous définir cela lorsque nous surchargons les opérateurs dans nos propres classes?
Phil
@teabaggs: Je ne peux pas facilement créer un lien pour le moment, et le lien serait simplement vers un document Word (que vous pouvez trouver assez facilement avec une recherche). Vous obtenez automatiquement des opérateurs levés lorsque vous définissez des surcharges d'opérateurs, le cas échéant.
Jon Skeet
116

La réponse de Jon est correcte mais j'ajouterais quelques notes supplémentaires.

Pourquoi Console.WriteLine(null)donne une erreur de compilation?

Il y a 19 surcharges de Console.WriteLineet trois d'entre elles sont applicables à a null: celle qui prend a string, celle qui prend a char[]et celle qui prend un object. C # ne peut pas déterminer lequel de ces trois vous voulez dire, il donne donc une erreur. Console.WriteLine((object)null)serait légal parce que maintenant c'est clair.

pourquoi Console.WriteLine(a)écrit une ligne vide?

aest un nul int?. La résolution de surcharge choisit la objectversion de la méthode, de sorte que le int?est encadré à une référence nulle. C'est donc fondamentalement le même que Console.WriteLine((object)null), qui écrit une ligne vide.

Pourquoi il n'y a pas NullReferenceExceptionsur l'incrément?

Où est la référence nulle qui vous inquiète? aest un null int?qui n'est pas un type de référence pour commencer! N'oubliez pas que les types valeur Nullable sont des types valeur , pas des types référence , donc ne vous attendez pas à ce qu'ils aient une sémantique de référence à moins qu'ils ne soient encadrés par un type référence. Il n'y a pas de boxe dans l'addition.

Eric Lippert
la source
0

Êtes-vous en train d'incrémenter null ???

int? a = null;
a++;

Cette déclaration signifie simplement null++nul + 1.

Selon ce document, un type Nullable peut représenter la plage correcte de valeurs pour son type de valeur sous-jacent, plus une valeur nulle supplémentaire. peut recevoir la valeur nulle

Ici, vous incrémentez null, puis il deviendra également une valeur nulle et non 0 ou tout autre entier.

Pourquoi il imprime en blanc au lieu d'erreur?

lorsque vous imprimez un type Nullable avec une valeur Null, il imprime un blanc au lieu d'une erreur car vous imprimez une variable, c'est-à-dire la valeur d'un emplacement mémoire. qui peut-être nul ou n'importe quel entier.

Mais lorsque vous essayez d'imprimer null en utilisant Console.WriteLine(null), comme null n'est pas une variable, il ne fait donc référence à aucun emplacement mémoire. Et par conséquent, cela donne une erreur "NullReferenceException".

Alors, comment pouvez-vous imprimer n'importe quel entier en utilisant Console.WriteLine(2);??

Dans ce cas, 2 sera mis en mémoire à un emplacement temporaire et le pointeur pointe vers cet emplacement de mémoire à imprimer.

Laxmikant Dange
la source