Warum löst das Inkrementieren von Nullable <int> keine Ausnahme aus?

98

Könnten Sie bitte erklären, warum Console.WriteLine eine leere Zeile schreibt ( Console.WriteLine(null)geben Sie mir einen Kompilierungsfehler) und warum es keine NullReferenceException gibt (auch a+=1wenn sie nicht ausgelöst werden sollte)?

int? a = null;
a++; // Why there is not NullReferenceException? 
Console.WriteLine(a); // Empty line
Maxim Zhukov
quelle
Alle drei Betreiber ++, +=und +haben Varianten angehoben. Daher sind die Aussagen a++;, a += 1;und a = a + 1;sind alle erlaubt. Jedes Produkt null(keine Ausnahme ausgelöst) aist anfangs null.
Jeppe Stig Nielsen
5
NullReferenceException? aber es int?ist kein Reference, es ist nur ein Wert int, der nullWert
annehmen

Antworten:

124

Sie beobachten die Auswirkungen eines angehobenen Bedieners .

Aus Abschnitt 7.3.7 der C # 5-Spezifikation:

Mit angehobenen Operatoren können vordefinierte und benutzerdefinierte Operatoren, die mit nicht nullbaren Werttypen arbeiten, auch mit nullbaren Formen dieser Typen verwendet werden. Aufgehobene Operatoren bestehen aus vordefinierten und benutzerdefinierten Operatoren, die bestimmte Anforderungen erfüllen, wie im Folgenden beschrieben:

  • Für die unären Operatoren existiert + ++ - -- ! ~ eine angehobene Form eines Operators, wenn sowohl der Operandentyp als auch der Ergebnistyp nicht nullbare Werttypen sind. Die angehobene Form wird durch Hinzufügen eines einzelnen ?Modifikators zum Operanden- und Ergebnistyp erstellt. Der aufgehobene Operator erzeugt einen Nullwert, wenn der Operand null ist. Andernfalls packt der aufgehobene Operator den Operanden aus, wendet den zugrunde liegenden Operator an und umschließt das Ergebnis.

a++In diesem Fall handelt es sich also im Grunde genommen um einen Ausdruck mit dem Ergebnis null(als int?), und die Variable bleibt unberührt.

Wenn du anrufst

Console.WriteLine(a);

Das wird eingerahmt object, wodurch es in eine Nullreferenz konvertiert wird, die als leere Zeile gedruckt wird.

Jon Skeet
quelle
3
Gibt es eine Möglichkeit, "null" ohne Verwendung zu drucken Console.WriteLine(a == null ? "null" : a)?
Cole Johnson
5
@Cole Johnson: Console.WriteLine (a ?? "null"); :)
David Chappelle
2
@DavidChappelle Wenn avom Typ ist int?, würde ich sagen, a ?? "null"findet keinen gemeinsamen Typ für die beiden Operanden. Sie benötigen einen Hinweis, objectder der übliche Typ ist. Wirf also einen der beiden Operanden darauf. Das awird verpackt. Zum Beispiel ((object)a) ?? "null"oder a ?? (object)"null".
Jeppe Stig Nielsen
Super. können Sie auf die Spezifikation verlinken? Können wir dies definieren, wenn wir Operatoren in unseren eigenen Klassen überladen?
Phil
@teabaggs: Ich kann momentan nicht einfach verlinken, und der Link würde nur zu einem Word-Dokument führen (das Sie mit einer Suche leicht genug finden können). Sie erhalten automatisch angehobene Bediener, wenn Sie gegebenenfalls Bedienerüberlastungen definieren.
Jon Skeet
116

Jons Antwort ist richtig, aber ich würde einige zusätzliche Notizen hinzufügen.

Warum gibt Console.WriteLine(null)es einen Kompilierungsfehler?

Es gibt 19 Überladungen von Console.WriteLineund drei davon sind auf a anwendbar null: die, die a nimmt string, die, die a nimmt char[]und die, die a nimmt object. C # kann nicht bestimmen, welche dieser drei Sie meinen, daher gibt es einen Fehler. Console.WriteLine((object)null)wäre legal, denn jetzt ist es klar.

Warum schreibt Console.WriteLine(a)man eine leere Zeile?

aist eine Null int?. Die Überlastungsauflösung wählt die objectVersion der Methode aus, sodass die int?Box auf eine Nullreferenz gesetzt wird. Das ist also im Grunde dasselbe wie das Console.WriteLine((object)null), was eine leere Zeile schreibt.

Warum gibt es nicht NullReferenceExceptionauf dem Inkrement?

Wo ist die Null - Referenz , dass Sie besorgt sind? aist eine Null, int?die zunächst kein Referenztyp ist! Denken Sie daran, Nullable - Wertetypen sind Werttypen , nicht Referenztypen , also nicht erwarten , dass sie Referenzsemantik haben , wenn sie auf einen Referenztyp sind eingerahmt. Es gibt kein Boxen im Zusatz.

Eric Lippert
quelle
0

Erhöhen Sie null?

int? a = null;
a++;

Diese Aussage bedeutet einfach null++null + 1.

Gemäß diesem Dokument kann ein nullbarer Typ den korrekten Wertebereich für den zugrunde liegenden Wertetyp sowie einen zusätzlichen Nullwert darstellen. Einem nullbaren Wert, ausgesprochen "Nullable von Int32", kann ein beliebiger Wert von -2147483648 bis 2147483647 zugewiesen werden kann der Nullwert zugewiesen werden

Hier erhöhen Sie null, dann wird es auch null, nicht 0 oder eine andere ganze Zahl.

Warum wird anstelle von Fehler leer gedruckt?

Wenn Sie einen nullbaren Typ mit einem Nullwert drucken, wird dieser anstelle eines Fehlers leer gedruckt, da Sie eine Variable drucken, dh den Wert eines Speicherorts. was vielleicht null oder eine ganze Zahl ist.

Wenn Sie jedoch versuchen, null mit zu drucken Console.WriteLine(null), ist null keine Variable und bezieht sich daher nicht auf einen Speicherort. Und daher gibt es Fehler "NullReferenceException".

Wie kann man dann eine ganze Zahl mit drucken Console.WriteLine(2);?

In diesem Fall wird 2 an einer temporären Stelle gespeichert, und der Zeiger zeigt auf diese zu druckende Speicherstelle.

Laxmikant Dange
quelle