Warum verursacht .ToString () in einer Nullzeichenfolge einen Nullfehler, wenn .ToString () in einem nullbaren Int mit Nullwert einwandfrei funktioniert?


70

selectedItem hat zwei Felder:

  • int? _cost
  • string _serialNumber

In diesem Beispiel _costund _serialNumbervon selectedItemsind BEIDE null. Ich lese die Felder selectedItemüber ihre Eigenschaften durch und fülle Textfelder mit ihren Werten aus, wenn ...

TextBox1.Text = selectedItem.Cost.ToString(); //no error
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error

Ich verstehe, dass dies SerialNumber.ToString()redundant ist (weil es bereits eine Zeichenfolge ist), aber ich verstehe nicht, warum dies diese Ausnahme verursacht:

Das Objekt mit dem Wert "null" braucht einen Wert.

  • int? _cost ist nullbar und hat keinen Wert, gibt mir aber keine Ausnahme.
  • string _serialNumberist nullbar und hat keinen Wert, aber es gibt mir die Ausnahme.

Diese Frage berührt es, der Typ fragt im Wesentlichen das Gleiche, aber es gibt keine festgelegte Antwort, und sie erklärt auch nicht, warum ein Nullwert int? Kann ich beispielsweise .ToString()eine nullfähige int, aber keine null-Zeichenfolge verwenden?


Die Frage, mit der Sie verlinkt haben, ist überhaupt nicht dieselbe. Bei dieser Frage geht es darum, wie MessageBox.Showund String.Concatmit nullStrings gearbeitet wird.
Mark Byers

9
Beachten Sie, dass Sie das Wort "nullable" verwenden, um zwei völlig verschiedene Dinge zu bedeuten. int?ist ein Werttyp, der aufgerufen wird Nullable<T>und eine spezielle Behandlung für Nullwerte aufweist. stringist ein Referenztyp (obwohl ein etwas seltsamer), der tatsächlich einen Wert von haben kann null. Ein hat int?immer einen Wert, es hat nur eine besondere Art zu sagen: "Ich benehme mich nullgerade so."
Michael Edenfield

Antworten:


90

Da der stringTyp nullwirklich auf nichts zeigt, befindet sich kein Objekt im Speicher.
Aber int?type (nullable), auch wenn der Wert so eingestellt ist, dass er nullimmer noch auf ein Objekt zeigt.
Wenn Sie Jeffrey Richters "CLR via C #" lesen, werden Sie feststellen, dass nullfähige Typen nur Fassadenklassen für gängige Typen mit einigen gekapselten Logiken sind, um die Arbeit mit DB null bequemer zu machen.

Überprüfen Sie msdn , um mehr über nullfähige Typen zu erfahren.


8
Also im Grunde, weil es nicht wirklich null ist, sondern nur so tut, als ob es es wäre?
The Oddler

3
Ja, im Grunde handelt es sich um eine Struktur, die um ein int-Feld und ein Boolesches Zeichen gewickelt ist und angibt, ob diese Struktur auf den int-Wert gesetzt oder noch einheitlich ist, was null bedeutet. In diesem Thema hat Mark Gravell die Implementierung in den Nullable<T>Typ eingefügt .
Johnny_D

1
int?oder Int32?. NichtInt?
Turbanoff

3
Dies ist alles korrekt, aber beachten Sie auch, dass ausgelöst selectedItem.Cost.GetType() wird , wenn Costder Typ null ist int?. Der Unterschied besteht darin, dass ToStringes sich um eine virtuelle Methode handelt, die von überschrieben wird Nullable<T>und daher direkt aufgerufen int?werden kann. Andererseits GetTypeist eine nicht virtuelle Methode für eine Basisklasse von Nullable<T>( objecttatsächlich) deklariert . Daher muss die Laufzeit den Nullable<>Typ in diesen Klassentyp konvertieren , bevor er aufgerufen werden kann. Dies GetTypewird als Boxen bezeichnet , Nullable<>hat jedoch spezielle Boxregeln, sodass eine Nullreferenz erstellt wird.
Jeppe Stig Nielsen

Vielen Dank. Ich war am meisten verwirrt, warum MyNullableBool.ToStringzurückkehrt string.Emptyund nicht wirft, wenn MyNullableBoolnull ist.
ProfK

33

A Nullable<int>ist a structund kann nicht wirklich null sein. Ein Methodenaufruf für eine "null" -Struktur funktioniert also immer noch.

Es gibt etwas "Compiler-Magie", die _cost == nulleinen gültigen Ausdruck macht.


4
Es ist Compiler-Magie. Das Hinzufügen einer impliziten Konvertierung von nullzur Nullable<T>Verwendung von normalem C # -Code (dh Überladen von Operatoren) würde erfordern, dass Sie einen Typ aus dem Ausdruck " null" ableiten können, den Sie nicht ableiten können, da er keinen hat. Der Compiler verwandelt sich im Wesentlichen Nullable<T> foo = nullin Nullable<T> foo = default(Nullable<T>)und foo == nullin !foo.HasValue.
Adam Robinson

14

int?ist eigentlich kein Objekt für sich, aber es ist ein Nullable<int>Objekt.

Wenn Sie also deklarieren int? _Cost, deklarieren Sie tatsächlich Nullable<int> _Costund die Eigenschaft von _Cost.Valueist undefinednicht das _CostObjekt selbst.

Es ist eigentlich ein syntaktischer Zucker zu verwenden , non nullableTypen wie int, booloder decimalleicht.

Laut MSDN :

Die Syntax T?ist eine Abkürzung für System.Nullable<T>, wobei Tes sich um einen Werttyp handelt. Die beiden Formen sind austauschbar.


Technisch _Cost.Valueist undefiniert, wenn _Costnull ist (dh wenn _Cost.HasValuefalsch ist)
3Doubloons

3
@AlexBrault: _CostKann nicht wirklich sein, nullda es kein Referenztyp ist.
Comecme

@comecme: Also die Klammerklärung.
3Doubloons


2

Die Nullable ist eigentlich eine Struktur, die zwei Eigenschaften verfügbar macht: HasValue und Value. Wenn Sie dies tun, erhalten Sie folgende Fehlermeldung:

int? i = null;
i.Value.ToString()

Um zu überprüfen, ob Ihr int? hat einen Wert, auf den Sie zugreifen könneni.HasValue


2
Nicht ganz richtig. Dieser Code wirft nicht ein NullReferenceException, sondern ein InvalidOperationException. Siehe Nullable (T) .Value .
Comecme

1

Ich denke, der Grund dafür ist, dass der Compiler, wenn er auf einen primitiven Datentyp stößt, ihn in das entsprechende Objekt einschließt. Der Methodenaufruf toString () ist hier nur ein indirekter Aufruf (Umschließen und anschließendes Aufrufen der Methode), und die Ausnahme wird dort behandelt. Im Fall von String rufen wir die Methode direkt auf. Wenn die Methode auf eine Null zeigt, löst sie die Ausnahme aus.


1

Der Grund ist einfach. int?oder Nullable<int>ist eine Struktur oder ein Werttyp , es kann niemals null sein .

Was passiert also, wenn wir Folgendes tun:

int? _cost = null;

_costwird zwei Felder Valueund HasValue, wenn wir vergeben nullzu _costseiner HasValueFlagge wird gesetzt falseund das ValueFeld zugewiesen würde default(T)im Fall int?wäre es 0.

Nun , wenn wir rufen ToStringauf _cost, Nullable<T>hat eine Überschreibung Definition ToString, die , wenn wir betrachten , Microsoft bereitgestellte Quellenreferenz implementiert wie:

public override string ToString() {
    return HasValue ? value.ToString() : "";
}

Somit wird eine leere Zeichenfolge zurückgegeben, da diese _costzugewiesen ist null.

Nun kommt der Fall von string _serialNumber. Da stringes sich um eine Art Referenz und kann rein halten null. Wenn es gehalten wird, würde ein nullAufruf ToStringwie erwartet die Nullreferenzausnahme erzeugen.

Möglicherweise wird Folgendes angezeigt: Werttypen und Referenztypen - MSDN


0
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error

yiels Fehler, weil die Funktion ToString () aufgerufen wird, die Mitglied von System.String ist . Diese Funktion gibt diese Instanz von System.String zurück. Es wird keine tatsächliche Konvertierung durchgeführt. String ist auch ein Referenztyp. Ein Referenztyp enthält einen Zeiger auf einen anderen Speicherort, der die Daten enthält.

TextBox1.Text = selectedItem.Cost.ToString(); //no error

gibt keinen Fehler aus, da die Funktion ToString () aufgerufen wird, die Mitglied von System.Integer ist . Diese Funktion konvertiert den numerischen Wert dieser Instanz in die entsprechende Zeichenfolgendarstellung. Integer ist auch ein Werttyp. Ein Datentyp ist ein Werttyp, wenn er die Daten in seiner eigenen Speicherzuordnung enthält.

Der gleiche Funktionsname ToString (), führt jedoch eine andere Aufgabe aus.

String.ToString-Methode

Int32.ToString-Methode

Werttypen und Referenztypen

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.