Wie ist null + true eine Zeichenfolge?

112

trueWie ist null + trueeine Zeichenfolge, da es sich nicht um einen Zeichenfolgentyp handelt ?

string s = true;  //Cannot implicitly convert type 'bool' to 'string'   
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'

Was ist der Grund dafür?

Javed Akram
quelle
27
Sie tun etwas, das keinen Sinn ergibt, und mögen dann die vom Compiler generierte Nachricht nicht? Dies ist ein ungültiger C # -Code ... was genau wollten Sie damit machen?
Hogan
19
@Hogan: Der Code scheint zufällig und akademisch genug zu sein, um die Frage aus purer Neugier zu stellen. Nur meine Vermutung ...
BoltClock
8
null + true gibt 1 in JavaScript zurück.
Fatih Acet

Antworten:

147

So bizarr dies auch erscheinen mag, es folgt einfach den Regeln aus der C # -Sprachenspezifikation.

Aus Abschnitt 7.3.4:

Eine Operation der Form x op y, wobei op ein überladbarer binärer Operator ist, x ein Ausdruck vom Typ X ist und y ein Ausdruck vom Typ Y ist, wird wie folgt verarbeitet:

  • Der Satz von benutzerdefinierten Kandidatenoperatoren, die von X und Y für den Operationsoperator op (x, y) bereitgestellt werden, wird bestimmt. Die Menge besteht aus der Vereinigung der von X bereitgestellten Kandidatenoperatoren und der von Y bereitgestellten Kandidatenoperatoren, die jeweils nach den Regeln von §7.3.5 bestimmt werden. Wenn X und Y vom gleichen Typ sind oder wenn X und Y von einem gemeinsamen Basistyp abgeleitet sind, treten gemeinsam genutzte Kandidatenoperatoren in der kombinierten Menge nur einmal auf.
  • Wenn der Satz von benutzerdefinierten Kandidatenoperatoren nicht leer ist, wird dies zum Satz von Kandidatenoperatoren für die Operation. Andernfalls werden die vordefinierten Implementierungen des binären Operators op, einschließlich ihrer aufgehobenen Formen, zum Satz von Kandidatenoperatoren für die Operation. Die vordefinierten Implementierungen eines bestimmten Operators sind in der Beschreibung des Operators angegeben (§7.8 bis §7.12).
  • Die Überlastungsauflösungsregeln von §7.5.3 werden auf die Menge der Kandidatenoperatoren angewendet, um den besten Operator in Bezug auf die Argumentliste (x, y) auszuwählen, und dieser Operator wird das Ergebnis des Überlastungsauflösungsprozesses. Wenn die Überlastungsauflösung keinen einzelnen besten Operator auswählt, tritt ein Bindungszeitfehler auf.

Lassen Sie uns dies der Reihe nach durchgehen.

X ist hier der Nulltyp - oder überhaupt kein Typ, wenn Sie es so sehen möchten. Es werden keine Kandidaten bereitgestellt. Y ist bool, was keine benutzerdefinierten bereitstellt+ Operatoren . Der erste Schritt findet also keine benutzerdefinierten Operatoren.

Der Compiler fährt dann mit dem zweiten Aufzählungspunkt fort und betrachtet die vordefinierten Implementierungen des binären Operators + und ihre aufgehobenen Formen. Diese sind in Abschnitt 7.8.4 der Spezifikation aufgeführt.

Wenn Sie sich diese vordefinierten Operatoren ansehen, ist nur einer anwendbarstring operator +(string x, object y) . Der Kandidatensatz hat also einen einzigen Eintrag. Das macht den letzten Aufzählungspunkt sehr einfach ... Überlastungsauflösung wählt diesen Operator aus und gibt einen Gesamtausdruckstyp von anstring .

Ein interessanter Punkt ist, dass dies auch dann auftritt, wenn für nicht erwähnte Typen andere benutzerdefinierte Operatoren verfügbar sind. Beispielsweise:

// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;

Das ist in Ordnung, wird aber nicht für ein Null-Literal verwendet, da der Compiler nicht weiß, wie er nachsehen soll Foo. Es muss nur berücksichtigt werden, stringda es sich um einen vordefinierten Operator handelt, der explizit in der Spezifikation aufgeführt ist. (Tatsächlich ist es kein Operator, der durch den Zeichenfolgentyp definiert ist ... 1 ) Das bedeutet, dass dies nicht kompiliert werden kann:

// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;

Andere Typen von zweiten Operanden verwenden natürlich einige andere Operatoren:

var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>

1 Sie fragen sich möglicherweise, warum es keinen String + -Operator gibt. Es ist eine vernünftige Frage, und ich bin nur raten , auf die Antwort, aber bedenken Sie diesen Ausdruck:

string x = a + b + c + d;

Wenn stringder C # -Compiler kein spezielles Gehäuse hätte, wäre dies genauso effektiv:

string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;

Das hat also zwei unnötige Zwischenzeichenfolgen erzeugt. Da es jedoch innerhalb der Compiler spezielle Unterstützung, es ist tatsächlich die Lage , die wie oben zu kompilieren:

string x = string.Concat(a, b, c, d);

Dadurch kann nur eine einzelne Zeichenfolge mit genau der richtigen Länge erstellt werden, wobei alle Daten genau einmal kopiert werden. Nett.

Jon Skeet
quelle
Ich stimme einfach nicht zu. Der Fehler liegt darin, dass er truenicht konvertierbar ist string. Wenn der Ausdruck gültig wäre string, wäre der Typ , aber in diesem Fall macht der Fehler beim Konvertieren in einen String den gesamten Ausdruck zu einem Fehler und hat daher keinen Typ.
Leppie
6
@leppie: Der Ausdruck ist gültig und sein Typ ist string. Versuchen Sie "var x = null + true;" - Es wird kompiliert und xist vom Typ string. Beachten Sie, dass die hier verwendete Signatur lautet string operator+(string, object)- sie wird boolin object(was in Ordnung ist) konvertiert , nicht in string.
Jon Skeet
Danke Jon, verstehe jetzt. Übrigens Abschnitt 14.7.4 in Version 2 der Spezifikation.
Leppie
@leppie: Ist das in der ECMA-Nummerierung? Das war schon immer ganz anders :(
Jon Skeet
47
in 20 Minuten. Er hat das Ganze in 20 Minuten geschrieben. Er sollte ein Buch schreiben oder so ... oh warte.
Epaga
44

Der Grund dafür ist, dass nach der Einführung der +dann die Bindungsregeln für den C # -Operator ins Spiel kommen. Dabei werden die +verfügbaren Operatoren berücksichtigt und die beste Überlastung ausgewählt. Einer dieser Operatoren ist der folgende

string operator +(string x, object y)

Diese Überladung ist mit den Argumenttypen im Ausdruck kompatibel null + true. Daher wird es als Operator ausgewählt und als wesentlich ((string)null) + truebewertet, was den Wert ergibt "True".

Abschnitt 7.7.4 der C # -Sprachenspezifikation enthält die Details zu dieser Auflösung.

JaredPar
quelle
1
Ich stelle fest, dass die generierte IL direkt zum Anruf bei Concat führt. Ich dachte, ich würde dort einen Aufruf der + Operatorfunktion sehen. Wäre das einfach Inlining-Optimierung?
Quentin-Starin
1
@qstarin Ich glaube, der Grund ist, dass es nicht wirklich ein operator+für gibt string. Stattdessen existiert es nur im Kopf des Compilers und übersetzt es einfach in Aufrufe fürstring.Concat
JaredPar
1
@qstarin @JaredPar: Ich habe in meiner Antwort etwas mehr darauf eingegangen.
Jon Skeet
11

Der Compiler macht sich auf die Suche nach einem Operator + (), der zuerst ein Nullargument annehmen kann. Keiner der Standardwerttypen ist qualifiziert, null ist für sie kein gültiger Wert. Die einzige Übereinstimmung ist System.String.operator + (), es gibt keine Mehrdeutigkeit.

Das zweite Argument dieses Operators ist ebenfalls eine Zeichenfolge. Das geht kapooey, kann Bool nicht implizit in String konvertieren.

Hans Passant
quelle
10

Interessanterweise wird mit Reflector der folgende Code überprüft:

string b = null + true;
Console.WriteLine(b);

wird vom Compiler in dieses umgewandelt:

Console.WriteLine(true);

Die Argumentation hinter dieser "Optimierung" ist etwas seltsam, muss ich sagen, und reimt sich nicht auf die Operatorauswahl, die ich erwarten würde.

Auch der folgende Code:

var b = null + true; 
var sb = new StringBuilder(b);

verwandelt sich in

string b = true; 
StringBuilder sb = new StringBuilder(b);

wo string b = true;wird eigentlich vom Compiler nicht akzeptiert.

Peter Lillevold
quelle
8

nullwird in eine Nullzeichenfolge umgewandelt, und es gibt einen impliziten Konverter von bool in eine Zeichenfolge, sodass die truein eine Zeichenfolge umgewandelt wird und dann+ Operator angewendet: Es ist wie folgt: Zeichenfolge str = "" + true.ToString ();

wenn Sie es mit Ildasm überprüfen:

string str = null + true;

es ist wie folgt:

.locals init ([0] string str)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  box        [mscorlib]System.Boolean
  IL_0007:  call       string [mscorlib]System.String::Concat(object)
  IL_000c:  stloc.0
Saeed Amiri
quelle
5
var b = (null + DateTime.Now); // String
var b = (null + 1);            // System.Nullable<Int32> | same with System.Single, System.Double, System.Decimal, System.TimeSpan etc
var b = (null + new Object()); // String | same with any ref type

Verrückt?? Nein, es muss einen Grund dafür geben.

Jemand ruft an Eric Lippert...

Decyklon
quelle
5

Der Grund dafür ist die Bequemlichkeit (das Verketten von Zeichenfolgen ist eine häufige Aufgabe).

Wie BoltClock sagte, ist der Operator '+' für numerische Typen und Zeichenfolgen definiert und kann auch für unsere eigenen Typen definiert werden (Operatorüberladung).

Wenn die Typen des Arguments keinen überladenen Operator '+' enthalten und es sich nicht um numerische Typen handelt, verwendet der Compiler standardmäßig die Verkettung von Zeichenfolgen.

Der Compiler fügt einen Aufruf ein, String.Concat(...)wenn Sie mit '+' verketten, und die Implementierung von Concat ruft ToString für jedes übergebene Objekt auf.

Quentin-Starin
quelle