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 string
perché è 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 string
non 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.