Perché è valido concatenare stringhe null ma non chiamare "null.ToString ()"?


126

Questo è un codice C # valido

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

Questo non è un codice C # valido

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

Perché la prima affermazione è valida?


97
Trovo strano che ti null.ToString()venga dato il nome wtf. Perché ti sorprende? Non puoi chiamare un metodo di istanza quando non hai nulla da cui chiamarlo in primo luogo.
BoltClock

11
@BoltClock: ovviamente è possibile chiamare un metodo di istanza su un'istanza nulla. Semplicemente impossibile in C #, ma molto valido sul CLR :)
leppie

1
Le risposte riguardo String.Concat sono quasi corrette. In effetti, l'esempio specifico nella domanda è quello del ripiegamento costante e i null nella prima riga vengono eliminati dal compilatore, ovvero non vengono mai valutati in fase di esecuzione perché non esistono più, il compilatore li ha cancellati. Ho scritto un piccolo monologo su tutte le regole per la concatenazione e il costante ripiegamento delle stringhe qui per maggiori informazioni: stackoverflow.com/questions/9132338/… .
Chris Shain,

77
non puoi aggiungere nulla a una stringa ma non puoi creare una stringa dal nulla.
RGB,

La seconda affermazione non è valida? class null_extension { String ToString( Object this arg ) { return ToString(arg); } }
ctrl-alt-delor

Risposte:


163

Il motivo del primo lavoro:

Da MSDN :

Nelle operazioni di concatenazione di stringhe, il compilatore C # tratta una stringa null come una stringa vuota, ma non converte il valore della stringa null originale.

Ulteriori informazioni sull'operatore + binario :

L'operatore binario + esegue la concatenazione di stringhe quando uno o entrambi gli operandi sono di tipo stringa.

Se un operando di concatenazione di stringhe è nullo, viene sostituita una stringa vuota. Altrimenti, qualsiasi argomento non stringa viene convertito nella sua rappresentazione stringa richiamando il ToStringmetodo virtuale ereditato dall'oggetto tipo.

Se ToStringrestituisce null, viene sostituita una stringa vuota.

Il motivo dell'errore nel secondo è:

null (riferimento C #) - La parola chiave null è un valore letterale che rappresenta un riferimento null, che non fa riferimento a nessun oggetto. null è il valore predefinito delle variabili del tipo di riferimento.


1
Funzionerebbe allora? var wtf = ((String)null).ToString();Sto lavorando a Java di recente, dove è possibile eseguire il casting di null, è da un po 'che non lavoro con C #.
Jochem,

14
@Jochem: stai ancora cercando di chiamare un metodo su un oggetto null, quindi suppongo di no. Pensate a come null.ToString()vs ToString(null).
Svish,

@Svish sì, ora ci ripenso, è un oggetto nullo, quindi hai ragione, non funzionerà. Neanche in Java: eccezione puntatore null. Non importa. Grazie per la tua risposta! [modifica: testato in Java: NullPointerException. Con la differenza che con il cast si compila, senza il cast non lo fa].
Jochem,

generalmente non vi è motivo di concatenare intenzionalmente o ToString la parola chiave null. Se hai bisogno di una stringa vuota usa string.Empty. se devi controllare se una variabile stringa è nulla puoi usare (myString == null) o string.IsNullOrEmpty (myString). In alternativa, per trasformare una variabile stringa null in stringa.Empty usa myNewString = (myString == null ?? string.Empty)
csauve

@Jochem casting lo farà compilare ma lancerà effettivamente NullReferenceException in fase di esecuzione
jeroenh

108

Perché l' +operatore in C # si traduce internamente in String.Concat, che è un metodo statico. E questo metodo sembra trattare nullcome una stringa vuota. Se guardi la fonte di String.Concatin Reflector, la vedrai:

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

(Anche MSDN lo menziona: http://msdn.microsoft.com/en-us/library/k9c94ey1.aspx )

D'altra parte, ToString()è un metodo di istanza, che non è possibile chiamare null(per quale tipo dovrebbe essere usato null?).


2
Non c'è nessun operatore + nella classe String però. Quindi è il compilatore che traduce in String.Concat?
superlogico

@superlogical Sì, è quello che fa il compilatore. Quindi, in realtà, l' +operatore di stringhe in C # è solo zucchero sintattico per String.Concat.
Botz3000,

Aggiungendo a ciò: il compilatore rileva automaticamente il sovraccarico di Concat che ha più senso. I sovraccarichi disponibili sono 1, 2 o 3 parametri oggetto, 4 parametri oggetto + __argliste una paramsversione di array di oggetti.
Wormbo,

Quale array di stringhe viene modificato lì? Fa Concatsempre creare un nuovo array di stringhe non nulli anche quando dato un array di stringhe non nulle, o è qualcos'altro?
supercat

@supercat L'array modificato è un nuovo array, che viene quindi passato a un metodo di supporto privato. Puoi guardare tu stesso il codice usando reflector.
Botz3000,

29

Il primo campione sarà tradotto in:

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

Il Concatmetodo controlla l'input e traduce null come stringa vuota

Il secondo campione sarà tradotto in:

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

Quindi nullqui verrà generata un'eccezione di riferimento


In realtà, AccessViolationExceptionverrà lanciato un testamento :)
leppie

L'ho appena fatto :) ((object)null).ToString()=> AccessViolation ((object)1 as string).ToString()=>NullReference
leppie

1
Ho NullReferenceException. DN 4.0
Viacheslav Smityukh,

Interessante. Quando lo metto ((object)null).ToString()dentro try/catchlo capisco NullReferenceExceptionanche io .
leppie,

3
@Ieppie tranne che non genera neanche AccessViolation (perché dovrebbe?) - NullReferenceException qui.
jeroenh,

11

La prima parte del tuo codice viene trattata in questo modo in String.Concat,

che è ciò che il compilatore C # chiama quando aggiungi stringhe. " abc" + nullviene tradotto in String.Concat("abc", null),

e internamente, quel metodo sostituisce nullcon String.Empty. Quindi, ecco perché la tua prima parte di codice non genera alcuna eccezione. è proprio come

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

E nella seconda parte del codice genera un'eccezione perché "null" non è un oggetto, la parola chiave null è un valore letterale che rappresenta un riferimento null , uno che non si riferisce a nessun oggetto. null è il valore predefinito delle variabili del tipo di riferimento.

E ' ToString()' un metodo che può essere chiamato da un'istanza di un oggetto ma non letterale.


3
String.Emptynon è uguale a null. È trattato allo stesso modo in alcuni metodi come String.Concat. Ad esempio, se hai una variabile stringa impostata su null, C # non la sostituirà con String.Emptyse provi a chiamare un metodo su di essa.
Botz3000,

3
Quasi. nullnon è trattato come String.Emptyda C #, è semplicemente trattato come quello in String.Concat, che è ciò che il compilatore C # chiama quando aggiungi stringhe. "abc" + nullviene tradotto String.Concat("abc", null)e internamente quel metodo sostituisce nullcon String.Empty. La seconda parte è completamente corretta.
Botz3000,

9

Nel framework COM che ha preceduto .net, era necessario che qualsiasi routine che ricevesse una stringa lo liberasse quando veniva eseguita con esso. Poiché era molto comune che le stringhe vuote passassero dentro e fuori le routine e poiché il tentativo di "liberare" un puntatore null fosse definito come un'operazione legittima di non fare nulla, Microsoft decise di avere un puntatore stringa null che rappresentasse una stringa vuota.

Per consentire una certa compatibilità con COM, molte routine in .net interpretano un oggetto null come una rappresentazione legale come una stringa vuota. Con un paio di lievi modifiche .net e i suoi linguaggi (in particolare permettendo ai membri dell'istanza di indicare "non invocare come virtuale"), Microsoft avrebbe potuto far sì che gli nulloggetti del tipo dichiarato String si comportassero ancora di più come stringhe vuote. Se Microsoft lo avesse fatto, avrebbe anche dovuto fare Nullable<T>un lavoro in qualche modo diverso (in modo da consentire - Nullable<String>qualcosa che avrebbero dovuto fare IMHO comunque) e / o definire un NullableStringtipo che sarebbe stato per lo più intercambiabile String, ma che non riguarderebbe un nullcome stringa vuota valida.

Così com'è, ci sono alcuni contesti in cui a nullsarà considerata una stringa vuota legittima e altri in cui non lo sarà. Non una situazione terribilmente utile, ma di cui i programmatori dovrebbero essere consapevoli. In generale, le espressioni del modulo stringValue.someMemberfalliranno se lo stringValueè null, ma la maggior parte dei metodi e degli operatori del framework che accettano stringhe come parametri considereranno nulluna stringa vuota.


5

'+'è un operatore infix. Come ogni operatore, sta davvero chiamando un metodo. Potresti immaginare una versione non infissa"wow".Plus(null) == "wow"

L'implementatore ha deciso qualcosa del genere ...

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

Quindi ... il tuo esempio diventa

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

che è lo stesso di

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

Nulla diventa mai una stringa. Cosìnull.ToString() non è diverso null.VoteMyAnswer(). ;)


Davvero, è più simile var bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");. I sovraccarichi degli operatori sono i metodi statici alla base: msdn.microsoft.com/en-us/library/s53ehcz3(v=VS.71).aspx Se non lo fossero, var bob = null + "abc";o soprattutto string bob = null + null;non sarebbero validi.
Ben Mosher,

Hai ragione, ho sentito che sarei stato troppo confuso se lo spiegassi in quel modo.
Nigel Thorne,

3

Immagino perché è un letterale che non si riferisce a nessun oggetto. ToString()ha bisogno di un object.


3

Qualcuno ha detto in questo thread di discussione che non puoi creare una stringa dal nulla. (che è una bella frase come penso). Ma sì, puoi :-), come mostra il seguente esempio:

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

funziona bene e non genera alcuna eccezione. L'unica differenza è che devi lanciare uno dei null in una stringa - se rimuovi la (stringa) cast , l'esempio viene comunque compilato, ma viene generata un'eccezione di runtime: "Operatore '+' è ambiguo su operandi di digitare '<null>' e '<null>' ".

NB Nell'esempio di codice sopra riportato, il valore di x non è nullo come ci si potrebbe aspettare, in realtà è una stringa vuota dopo aver eseguito il cast di uno degli operandi in una stringa.


Un altro fatto interessante è che in C # / .NET il modo in cui nullviene trattato non è sempre lo stesso se si considerano tipi di dati diversi. Per esempio:

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

Per quanto riguarda la prima riga dello snippet di codice: se si xtratta di una variabile intera nullable (ovvero int?) che contiene valore 1, si ottiene il risultato<null> . Se è una stringa (come mostrato nel commento) con valore "1", allora stai "1"tornando piuttosto che<null> .

NB Interessante anche: se si utilizza var x = 1;per la prima riga, viene visualizzato un errore di runtime. Perché? Perché l'assegnazione trasformerà la variabile xnel tipo di dati int, che non è nullable. Il compilatore non assume int?qui, e quindi fallisce nella seconda riga dove nullviene aggiunto.


2

L'aggiunta nulla una stringa viene semplicemente ignorata. null(nel tuo secondo esempio) non è un'istanza di nessun oggetto, quindi non ha nemmeno un ToString()metodo. È solo un letterale.


1

Perché non c'è differenza tra string.Emptye nullquando concateni le stringhe. Puoi anche passare a null string.Format. Ma stai provando a chiamare un metodo null, che si tradurrebbe sempre in a NullReferenceExceptione quindi genererebbe un errore del compilatore.
Se per qualche motivo vuoi davvero farlo, potresti scrivere un metodo di estensione, che controlla nulle poi ritorna string.Empty. Ma un'estensione del genere dovrebbe essere usata solo quando assolutamente necessario (secondo me).


0

Come generale: può essere valido accettare o meno null come parametro a seconda delle specifiche, ma è sempre invalido chiamare un metodo su null.


Questo è un altro argomento per cui gli operandi dell'operatore + possono essere nulli in caso di stringhe. Questa è una specie di cosa VB (scusate ragazzi) per rendere la vita dei programmatori più semplice, o supponendo che il programmatore non possa gestire i null. Non sono completamente d'accordo con questa specifica. 'sconosciuto' + 'nulla' dovrebbe essere ancora 'sconosciuto' ...

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.