Warum ist es gültig, Nullzeichenfolgen zu verketten, aber nicht "null.ToString ()" aufzurufen?

126

Dies ist ein gültiger C # -Code

var bob = "abc" + null + null + null + "123";  // abc123

Dies ist kein gültiger C # -Code

var wtf = null.ToString(); // compiler error

Warum ist die erste Aussage gültig?

superlogisch
quelle
97
Ich finde es merkwürdig, dass dir null.ToString()der Name gegeben wird wtf. Warum überrascht dich das? Sie können eine Instanzmethode nicht aufrufen, wenn Sie überhaupt nichts zum Aufrufen haben.
BoltClock
11
@BoltClock: Natürlich ist es möglich, eine Instanzmethode für eine Nullinstanz aufzurufen. Nur nicht möglich in C #, aber sehr gültig in der CLR :)
Leppie
1
Die Antworten in Bezug auf String.Concat sind fast richtig. Tatsächlich handelt es sich bei dem spezifischen Beispiel in der Frage um eine konstante Faltung , und die Nullen in der ersten Zeile werden vom Compiler entfernt, dh sie werden zur Laufzeit nie ausgewertet, weil sie nicht mehr vorhanden sind. Der Compiler hat sie gelöscht. Ich habe hier einen kleinen Monolog über alle Regeln für die Verkettung und das ständige Falten von Strings geschrieben, um weitere Informationen zu erhalten: stackoverflow.com/questions/9132338/… .
Chris Shain
77
Sie können einer Zeichenfolge nichts hinzufügen, aber Sie können aus nichts eine Zeichenfolge machen.
RGB
Ist die zweite Aussage ungültig? class null_extension { String ToString( Object this arg ) { return ToString(arg); } }
Strg-Alt-Delor

Antworten:

163

Der Grund für die erste Arbeit:

Von MSDN :

Bei Verkettungsoperationen von Zeichenfolgen behandelt der C # -Compiler eine Nullzeichenfolge genauso wie eine leere Zeichenfolge, konvertiert jedoch nicht den Wert der ursprünglichen Nullzeichenfolge.

Weitere Informationen zum + Binäroperator :

Der Operator binär + führt eine Zeichenfolgenverkettung durch, wenn einer oder beide Operanden vom Typ Zeichenfolge sind.

Wenn ein Operand der Zeichenfolgenverkettung null ist, wird eine leere Zeichenfolge ersetzt. Andernfalls wird jedes Nicht-String-Argument durch Aufrufen der ToStringvom Typ-Objekt geerbten virtuellen Methode in seine String-Darstellung konvertiert .

Wenn ToStringzurückgegeben wird null, wird eine leere Zeichenfolge ersetzt.

Der Grund für den Fehler in der Sekunde ist:

null (C # -Referenz) - Das Schlüsselwort null ist ein Literal, das eine Nullreferenz darstellt, die sich nicht auf ein Objekt bezieht. null ist der Standardwert von Variablen vom Referenztyp.

Pranay Rana
quelle
1
Würde das dann funktionieren? var wtf = ((String)null).ToString();Ich arbeite kürzlich in Java, wo das Casting von Nullen möglich ist. Es ist eine Weile her, seit ich mit C # gearbeitet habe.
Jochem
14
@Jochem: Sie versuchen immer noch, eine Methode für ein Nullobjekt aufzurufen, also rate ich nein. Betrachten Sie es als null.ToString()vs ToString(null).
Svish
@Svish ja, jetzt denke ich wieder daran, es ist ein Null-Objekt, also hast du recht, es wird nicht funktionieren. In Java auch nicht: Nullzeigerausnahme. Keine Ursache. Tnx für deine Antwort! [edit: hat es in Java getestet: NullPointerException. Mit dem Unterschied, dass es mit cast kompiliert wird, ohne cast nicht].
Jochem
Im Allgemeinen gibt es keinen Grund, das Schlüsselwort null absichtlich zu konatieren oder zu ändern. Wenn Sie eine leere Zeichenfolge benötigen, verwenden Sie string.Empty. Wenn Sie überprüfen müssen, ob eine Zeichenfolgenvariable null ist, können Sie entweder (myString == null) oder string.IsNullOrEmpty (myString) verwenden. Alternativ können Sie eine Null-String-Variable in einen String umwandeln. Leer verwenden Sie myNewString = (myString == null ?? string.Empty)
csauve
@Jochem Casting wird es kompilieren lassen, aber es wird tatsächlich eine NullReferenceException zur Laufzeit
auslösen
108

Weil der +Operator in C # intern in übersetzt String.Concat, was eine statische Methode ist. Und diese Methode wird zufällig nullwie eine leere Zeichenfolge behandelt. Wenn Sie sich die Quelle von String.Concatin Reflector ansehen , werden Sie es sehen:

// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array

(MSDN erwähnt es auch: http://msdn.microsoft.com/en-us/library/k9c94ey1.aspx )

Auf der anderen Seite ToString()handelt es sich um eine Instanzmethode, die Sie nicht aufrufen können null(für welchen Typ sollte sie verwendet werden null?).

Botz3000
quelle
2
Es gibt jedoch keinen + -Operator in der String-Klasse. Ist es also der Compiler, der in String.Concat übersetzt?
Superlogical
@superlogical Ja, das macht der Compiler. Tatsächlich ist der +Operator für Zeichenfolgen in C # nur syntaktischer Zucker für String.Concat.
Botz3000
Hinzu kommt: Der Compiler wählt automatisch die Überladung von Concat aus, die am sinnvollsten ist. Verfügbare Überladungen sind 1, 2 oder 3 Objektparameter, 4 Objektparameter + __arglistund eine paramsObjektarray-Version.
Wormbo
Welches String-Array wird dort geändert? Erstellt Concatimmer ein neues Array von Nicht-Null-Zeichenfolgen, selbst wenn ein Array von Nicht-Null-Zeichenfolgen angegeben wird, oder ist etwas anderes im Gange?
Supercat
@supercat Das geänderte Array ist ein neues Array, das dann an eine private Hilfsmethode übergeben wird. Sie können den Code selbst mit einem Reflektor anzeigen.
Botz3000
29

Das erste Beispiel wird übersetzt in:

var bob = String.Concat("abc123", null, null, null, "abs123");

Die ConcatMethode überprüft die Eingabe und übersetzt null als leere Zeichenfolge

Das zweite Beispiel wird übersetzt in:

var wtf = ((object)null).ToString();

Hier wird also eine nullReferenzausnahme generiert

Viacheslav Smityukh
quelle
Eigentlich wird ein AccessViolationExceptiongeworfen :)
Leppie
Ich habe es gerade getan :) ((object)null).ToString()=> AccessViolation ((object)1 as string).ToString()=>NullReference
Leppie
1
Ich habe NullReferenceException. DN 4.0
Viacheslav Smityukh
Interessant. Wenn ich ((object)null).ToString()in ein try/catchstecke, bekomme ich NullReferenceExceptionauch.
Leppie
3
@Ieppie, außer dass AccessViolation auch nicht ausgelöst wird (warum sollte das so sein?) - NullReferenceException hier.
Jeroenh
11

Der erste Teil Ihres Codes wird einfach so behandelt in String.Concat,

Dies ist, was der C # -Compiler aufruft, wenn Sie Zeichenfolgen hinzufügen. " abc" + nullwird übersetzt in String.Concat("abc", null),

und intern ersetzt diese Methode nulldurch String.Empty. Deshalb löst Ihr erster Teil des Codes keine Ausnahme aus. es ist einfach so

var bob = "abc" + string.Empty + string.Empty + string.Empty + "123";  //abc123

Und im zweiten Teil Ihres Codes wird eine Ausnahme ausgelöst, da 'null' kein Objekt ist. Das Schlüsselwort null ist ein Literal, das eine Nullreferenz darstellt , die auf kein Objekt verweist. null ist der Standardwert von Variablen vom Referenztyp.

Und ' ToString()' ist eine Methode, die von einer Instanz eines Objekts aufgerufen werden kann, jedoch kein Literal.

Talha
quelle
3
String.Emptyist nicht gleich null. Es wird in einigen Methoden genauso behandelt wie String.Concat. Wenn Sie beispielsweise eine Zeichenfolgenvariable auf gesetzt haben null, wird diese durch C # nicht ersetzt, String.Emptywenn Sie versuchen, eine Methode dafür aufzurufen.
Botz3000
3
Fast. nullwird nicht wie String.Emptyvon C # behandelt, sondern nur wie in String.ConcatC, was der C # -Compiler beim Hinzufügen von Zeichenfolgen aufruft. "abc" + nullwird übersetzt String.Concat("abc", null)und intern ersetzt diese Methode nulldurch String.Empty. Der zweite Teil ist völlig richtig.
Botz3000
9

In dem COM-Framework, das vor .net stand, musste jede Routine, die eine Zeichenfolge erhielt, diese freigeben, wenn sie damit fertig war. Da es sehr häufig vorkam, dass leere Zeichenfolgen in Routinen und aus Routinen übergeben wurden und der Versuch, einen Nullzeiger "freizugeben", als legitime Nichtstun-Operation definiert wurde, entschied Microsoft, dass ein Nullzeichenfolgenzeiger eine leere Zeichenfolge darstellt.

Um eine gewisse Kompatibilität mit COM zu gewährleisten, interpretieren viele Routinen in .net ein Nullobjekt als rechtliche Darstellung als leere Zeichenfolge. Mit ein paar geringfügigen Änderungen an .net und seinen Sprachen (insbesondere wenn Instanzmitglieder angeben können, dass sie nicht als virtuell aufgerufen werden sollen) hätte Microsoft nullObjekte vom deklarierten Typ String noch mehr wie leere Strings verhalten können. Wenn Microsoft das getan hätte, hätte es auch die Nullable<T>Arbeit etwas anders gestalten müssen (um zuzulassen - etwas, Nullable<String>was sie meiner Meinung nach sowieso hätten tun sollen) und / oder einen NullableStringTyp definieren müssen, der größtenteils austauschbar Stringwäre, aber a nicht berücksichtigt nullals gültige leere Zeichenfolge.

So wie es ist, gibt es einige Kontexte, in denen ein nullWille als legitimer leerer String angesehen wird, und andere, in denen dies nicht der Fall ist. Keine schrecklich hilfreiche Situation, aber eine, die Programmierer kennen sollten. Im Allgemeinen schlagen Ausdrücke des Formulars stringValue.someMemberfehl, wenn dies der Fall stringValueist null. Die meisten Framework-Methoden und -Operatoren, die Zeichenfolgen als Parameter akzeptieren, werden jedoch nullals leere Zeichenfolge betrachtet.

Superkatze
quelle
5

'+'ist ein Infix-Operator. Wie jeder Operator ruft er wirklich eine Methode auf. Sie können sich eine Nicht-Infix-Version vorstellen"wow".Plus(null) == "wow"

Der Implementierer hat sich für so etwas entschieden ...

class String
{
  ...
  String Plus(ending)
  {
     if(ending == null) return this;
     ...
  }
} 

Also ... dein Beispiel wird

var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123");  // abc123

das ist das gleiche wie

var bob = "abc".Plus("123");  // abc123

Null wird zu keinem Zeitpunkt zu einer Zeichenfolge. Das null.ToString()ist also nicht anders null.VoteMyAnswer(). ;)

Nigel Thorne
quelle
Wirklich, es ist eher so var bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");. Operatorüberladungen sind im Kern statische Methoden: msdn.microsoft.com/en-us/library/s53ehcz3(v=VS.71).aspx Wären sie nicht var bob = null + "abc";oder string bob = null + null;wären sie insbesondere nicht gültig.
Ben Mosher
Du hast recht, ich hatte nur das Gefühl, ich wäre zu verwirrend, wenn ich es so erkläre.
Nigel Thorne
3

Ich denke, weil es ein Literal ist, das sich auf kein Objekt bezieht. ToString()braucht eine object.

Saeed Neamati
quelle
3

Jemand sagte in diesem Diskussionsthread, dass man aus nichts einen String machen kann. (Das ist ein schöner Satz, wie ich denke). Aber ja - Sie können :-), wie das folgende Beispiel zeigt:

var x = null + (string)null;     
var wtf = x.ToString();

funktioniert gut und löst überhaupt keine Ausnahme aus. Der einzige Unterschied besteht darin, dass Sie eine der Nullen in eine Zeichenfolge umwandeln müssen. Wenn Sie die Umwandlung (Zeichenfolge) entfernen , wird das Beispiel weiterhin kompiliert, es wird jedoch eine Laufzeitausnahme ausgelöst : "Operator '+' ist für Operanden von nicht eindeutig Geben Sie '<null>' und '<null>' "ein.

Hinweis: Im obigen Codebeispiel ist der Wert von x nicht wie erwartet null, sondern eine leere Zeichenfolge, nachdem Sie einen der Operanden in eine Zeichenfolge umgewandelt haben.


Eine weitere interessante Tatsache ist, dass in C # / .NET die nullBehandlung nicht immer gleich ist, wenn Sie unterschiedliche Datentypen betrachten. Beispielsweise:

int? x = 1;  //  string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());

Betrachten Sie die erste Zeile des Code-Snippets: Wennx es sich um eine nullbare Ganzzahlvariable (dh int?) handelt, die einen Wert enthält 1, erhalten Sie das Ergebnis <null>zurück. Wenn es sich um eine Zeichenfolge (wie im Kommentar gezeigt) mit Wert handelt "1", erhalten Sie "1"eher zurück als <null>.

NB Auch interessant: Wenn Sie var x = 1;für die erste Zeile verwenden, wird ein Laufzeitfehler angezeigt. Warum? Weil die Zuweisung die Variable xin den Datentyp verwandelt int, der nicht nullwertfähig ist. Der Compiler geht hier nicht davon int?aus und schlägt daher in der 2. Zeile wo fehlnull hinzugefügt wird.

Matt
quelle
2

Das Hinzufügen nullzu einer Zeichenfolge wird einfach ignoriert. null(in Ihrem zweiten Beispiel) ist keine Instanz eines Objekts, daher gibt es nicht einmal eine ToString()Methode. Es ist nur ein Wortlaut.

Thorsten Dittmar
quelle
1

Weil es keinen Unterschied zwischen string.Emptyund nullwann Sie Zeichenfolgen konkatieren. Sie können auch null übergeben string.Format. Sie versuchen jedoch, eine Methode aufzurufen null, die immer zu a führt NullReferenceExceptionund daher einen Compilerfehler generiert.
Wenn Sie es aus irgendeinem Grund wirklich tun möchten, können Sie eine Erweiterungsmethode schreiben, die nach sucht nullund dann zurückgibt string.Empty. Eine solche Erweiterung sollte aber nur verwendet werden, wenn dies absolut notwendig ist (meiner Meinung nach).

Franky
quelle
0

Allgemein: Je nach Spezifikation kann es gültig sein, null als Parameter zu akzeptieren, aber es ist immer ungültig, eine Methode für null aufzurufen.


Dies ist ein weiteres Thema, warum die Operanden des Operators + bei Zeichenfolgen null sein können. Dies ist eine Art VB-Sache (sorry Leute), um Programmierern das Leben zu erleichtern, oder wenn der Programmierer nicht mit Nullen umgehen kann. Ich stimme dieser Spezifikation überhaupt nicht zu. 'unbekannt' + 'irgendetwas' sollte immer noch 'unbekannt' sein ...

p.pickardou
quelle