In che modo null + true è una stringa?


112

Dato che truenon è un tipo stringa, com'ènull + true una stringa?

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

Qual è il motivo dietro questo?


27
Fai qualcosa che non ha senso e poi non ti piace il messaggio che genera il compilatore? Questo non è un codice C # non valido ... cosa volevi esattamente che facesse?
Hogan

19
@ Hogan: Il codice sembra abbastanza casuale e accademico da poter porre la domanda per pura curiosità. Immagino solo ...
BoltClock

8
null + true restituisce 1 in JavaScript.
Fatih Acet

Risposte:


147

Per quanto bizzarro possa sembrare, sta semplicemente seguendo le regole delle specifiche del linguaggio C #.

Dalla sezione 7.3.4:

Un'operazione della forma x op y, dove op è un operatore binario sovraccaricabile, x è un'espressione di tipo X e y è un'espressione di tipo Y, viene elaborata come segue:

  • Viene determinato l'insieme di operatori candidati definiti dall'utente forniti da X e Y per l'operatore operativo op (x, y). L'insieme è costituito dall'unione degli operatori candidati forniti da X e degli operatori candidati forniti da Y, ciascuno determinato utilizzando le regole del §7.3.5. Se X e Y sono dello stesso tipo o se X e Y derivano da un tipo di base comune, gli operatori candidati condivisi si verificano solo una volta nell'insieme combinato.
  • Se la serie di operatori candidati definiti dall'utente non è vuota, diventa la serie di operatori candidati per l'operazione. Altrimenti, le implementazioni op dell'operatore binario predefinito, comprese le loro forme sollevate, diventano l'insieme di operatori candidati per l'operazione. Le implementazioni predefinite di un dato operatore sono specificate nella descrizione dell'operatore (da §7.8 a §7.12).
  • Le regole di risoluzione del sovraccarico di §7.5.3 vengono applicate all'insieme di operatori candidati per selezionare l'operatore migliore rispetto alla lista di argomenti (x, y), e questo operatore diventa il risultato del processo di risoluzione del sovraccarico. Se la risoluzione dell'overload non riesce a selezionare un singolo operatore migliore, si verifica un errore di tempo di associazione.

Quindi, esaminiamolo a turno.

X è il tipo nullo qui - o non è affatto un tipo, se vuoi pensarlo in questo modo. Non fornisce candidati. Y è bool, che non fornisce alcun +operatore definito dall'utente . Quindi il primo passaggio non trova operatori definiti dall'utente.

Il compilatore passa quindi al secondo punto elenco, esaminando l'operatore binario predefinito + le implementazioni e le loro forme sollevate. Questi sono elencati nella sezione 7.8.4 delle specifiche.

Se guardi attraverso questi operatori predefiniti, l' unico applicabile è string operator +(string x, object y). Quindi l'insieme candidato ha una sola voce. Questo rende il punto finale molto semplice ... la risoluzione del sovraccarico sceglie quell'operatore, dando un tipo di espressione generale di string.

Un punto interessante è che ciò si verificherà anche se sono disponibili altri operatori definiti dall'utente su tipi non menzionati. Per esempio:

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

Va bene, ma non è usato per un valore letterale nullo, perché il compilatore non sa come cercare Foo. Sa considerare solo stringperché è un operatore predefinito elencato esplicitamente nelle specifiche. (In effetti, non è un operatore definito dal tipo di stringa ... 1 ) Ciò significa che non verrà compilato:

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

Altri tipi di secondo operando useranno altri operatori, ovviamente:

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 Forse ti starai chiedendo perché non c'è una stringa + operatore. È una domanda ragionevole, e sto solo indovinando la risposta, ma considera questa espressione:

string x = a + b + c + d;

Se stringnon avesse maiuscole e minuscole nel compilatore C #, questo finirebbe nel modo più efficace:

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

Quindi sono state create due stringhe intermedie non necessarie. Tuttavia, poiché esiste un supporto speciale all'interno del compilatore, è effettivamente in grado di compilare quanto sopra come:

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

che può creare solo una singola stringa della lunghezza esatta, copiando tutti i dati esattamente una volta. Bello.


'dare un'espressione generale di tipo stringa' Semplicemente non sono d'accordo. L'errore deriva dal truenon essere convertibile in string. Se l'espressione fosse valida il tipo sarebbe string, ma in questo caso la mancata conversione in stringa rende l'intera espressione un errore, e quindi non ha tipo.
leppie

6
@leppie: l'espressione è valida e il suo tipo è stringa. Prova "var x = null + true;" - si compila ed xè di tipo string. Nota che la firma usata qui è string operator+(string, object): si sta convertendo boolin object(che va bene), non in string.
Jon Skeet

Grazie Jon, ora capisci. Sezione BTW 14.7.4 nella versione 2 delle specifiche.
leppie

@leppie: è nella numerazione ECMA? È sempre stato molto diverso :(
Jon Skeet

47
in 20 minuti. Ha scritto tutto questo in 20 minuti. Dovrebbe scrivere un libro o qualcosa del genere ... oh aspetta.
Epaga

44

Il motivo è perché una volta introdotte le +regole di associazione dell'operatore C # entrano in gioco. Considererà l'insieme di +operatori disponibili e selezionerà il miglior sovraccarico. Uno di questi operatori è il seguente

string operator +(string x, object y)

Questo overload è compatibile con i tipi di argomento nell'espressione null + true. Quindi viene selezionato come operatore e viene valutato essenzialmente come quello ((string)null) + trueche valuta il valore "True".

La sezione 7.7.4 delle specifiche del linguaggio C # contiene i dettagli su questa risoluzione.


1
Ho notato che l'IL generato va direttamente a chiamare Concat. Stavo pensando di vedere una chiamata alla funzione + operator lì dentro. Sarebbe semplicemente un'ottimizzazione inline?
quentin-starin

1
@qstarin credo che il motivo sia che non c'è davvero un operator+per string. Invece esiste solo nella mente del compilatore e lo traduce semplicemente in chiamate perstring.Concat
JaredPar

1
@qstarin @JaredPar: ne ho parlato un po 'di più nella mia risposta.
Jon Skeet

11

Il compilatore esce alla ricerca di un operatore + () che può accettare prima un argomento nullo. Nessuno dei tipi di valore standard è qualificato, null non è un valore valido per loro. La sola e unica corrispondenza è System.String.operator + (), non c'è ambiguità.

Anche il secondo argomento di quell'operatore è una stringa. Questo va kapooey, non può convertire implicitamente bool in stringa.


10

È interessante notare che, utilizzando Reflector per ispezionare ciò che viene generato, il codice seguente:

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

viene trasformato in questo dal compilatore:

Console.WriteLine(true);

Il ragionamento alla base di questa "ottimizzazione" è un po 'strano, devo dire, e non fa rima con la selezione dell'operatore che mi aspetterei.

Inoltre, il codice seguente:

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

si trasforma in

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

dove in string b = true;realtà non è accettato dal compilatore.


8

nullverrà eseguito il cast su una stringa nulla e vi è un convertitore implicito da bool a stringa, quindi trueverrà eseguito il cast su stringa e quindi +verrà applicato l'operatore: è come: string str = "" + true.ToString ();

se lo controlli con Ildasm:

string str = null + true;

è come muggito:

.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

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

Pazzo ?? No, ci deve essere una ragione dietro.

Qualcuno chiama Eric Lippert...


5

La ragione di ciò è la comodità (la concatenazione di stringhe è un'attività comune).

Come ha detto BoltClock, l'operatore '+' è definito su tipi numerici, stringhe e può essere definito anche per i nostri tipi (sovraccarico dell'operatore).

Se non è presente un operatore "+" sovraccarico sui tipi di argomento e non sono tipi numerici, il compilatore utilizza per impostazione predefinita la concatenazione di stringhe.

Il compilatore inserisce una chiamata a String.Concat(...)quando si concatena utilizzando '+' e l'implementazione di Concat chiama ToString su ogni oggetto passato al suo interno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.