¿Por qué es válido concatenar cadenas nulas pero no llamar a "null.ToString ()"?


126

Este es un código C # válido

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

Este no es un código C # válido

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

¿Por qué es válida la primera declaración?


97
Me parece curioso que null.ToString()se le dé el nombre wtf. ¿Por qué te sorprende eso? No puede llamar a un método de instancia cuando no tiene nada para llamarlo en primer lugar.
BoltClock

11
@BoltClock: Por supuesto, es posible llamar a un método de instancia en una instancia nula. Simplemente no es posible en C #, pero es muy válido en CLR :)
leppie

1
Las respuestas con respecto a String.Concat son casi correctas. De hecho, el ejemplo específico en la pregunta es uno de plegado constante , y el compilador elimina los valores nulos en la primera línea, es decir, nunca se evalúan en tiempo de ejecución porque ya no existen, el compilador los ha borrado. Escribí un pequeño monólogo sobre todas las reglas para la concatenación y el plegado constante de cadenas aquí para obtener más información: stackoverflow.com/questions/9132338/… .
Chris Shain

77
no puede agregar nada a una cadena pero no puede hacer una cadena de la nada.
RGB

¿La segunda declaración no es válida? class null_extension { String ToString( Object this arg ) { return ToString(arg); } }
ctrl-alt-delor

Respuestas:


163

El motivo del primer trabajo:

De MSDN :

En las operaciones de concatenación de cadenas, el compilador de C # trata una cadena nula igual que una cadena vacía, pero no convierte el valor de la cadena nula original.

Más información sobre el operador binario + :

El operador binario + realiza la concatenación de cadenas cuando uno o ambos operandos son de tipo cadena.

Si un operando de concatenación de cadenas es nulo, se sustituye una cadena vacía. De lo contrario, cualquier argumento que no sea de cadena se convierte en su representación de cadena invocando el ToStringmétodo virtual heredado del objeto de tipo.

Si se ToStringdevuelve null, se sustituye una cadena vacía.

La razón del error en segundo es:

nulo (referencia de C #) : la palabra clave nula es un literal que representa una referencia nula, una que no hace referencia a ningún objeto. nulo es el valor predeterminado de las variables de tipo de referencia.


1
¿Funcionaría entonces? var wtf = ((String)null).ToString();Estoy trabajando en Java recientemente, donde es posible lanzar nulos, ha pasado un tiempo desde que trabajé con C #.
Jochem

14
@Jochem: Todavía estás intentando llamar a un método en un objeto nulo, así que supongo que no. Piense en ello como null.ToString()vs ToString(null).
Svish

@Svish sí, ahora lo pienso de nuevo, es un objeto nulo, así que tienes razón, no funcionará. Tampoco en Java: excepción de puntero nulo. No importa. Tnx por tu respuesta! [edit: probado en Java: NullPointerException. Con la diferencia de que con el elenco compila, sin el elenco no lo hace].
Jochem

generalmente no hay razón para concatenar intencionalmente o ToString la palabra clave nula. Si necesita una cadena vacía, use string.Empty. si necesita verificar si una variable de cadena es nula, puede usar (myString == null) o string.IsNullOrEmpty (myString). Alternativamente para transformar una variable de cadena nula en cadena. Vacío use myNewString = (myString == null ?? string.Empty)
csauve

El lanzamiento de @Jochem lo hará compilar, pero de hecho arrojará una NullReferenceException en tiempo de ejecución
jeroenh

108

Porque el +operador en C # se traduce internamente en String.Concat, que es un método estático. Y este método pasa a tratarse nullcomo una cadena vacía. Si miras la fuente de String.ConcatReflector, lo verás:

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

(MSDN también lo menciona: http://msdn.microsoft.com/en-us/library/k9c94ey1.aspx )

Por otro lado, ToString()es un método de instancia, al que no puede recurrir null(¿para qué tipo debe usarse null?).


2
Sin embargo, no hay un operador + en la clase String. Entonces, ¿es el compilador que se traduce a String.Concat?
superlogical

@superlogical Sí, eso es lo que hace el compilador. Entonces, en realidad, el +operador en cadenas en C # es solo azúcar sintáctico para String.Concat.
Botz3000

Además de eso: el compilador selecciona automáticamente la sobrecarga de Concat que tiene más sentido. Las sobrecargas disponibles son 1, 2 o 3 parámetros de objeto, 4 parámetros de objeto + __arglisty una paramsversión de matriz de objetos.
Wormbo

¿Qué conjunto de cadenas se está modificando allí? ¿ ConcatSiempre crea una nueva matriz de cadenas no nulas, incluso cuando se le da una matriz de cadenas no nulas, o está sucediendo algo más?
supercat

@supercat La matriz modificada es una nueva matriz, que luego se pasa a un método auxiliar privado. Puede mirar el código usted mismo usando el reflector.
Botz3000

29

La primera muestra se traducirá a:

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

El Concatmétodo verifica la entrada y traduce nulo como una cadena vacía

La segunda muestra se traducirá a:

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

Entonces nullse generará una excepción de referencia aquí


En realidad, se AccessViolationExceptionlanzará un :) :)
leppie

Acabo de hacerlo :) ((object)null).ToString()=> AccessViolation ((object)1 as string).ToString()=>NullReference
leppie

1
Tengo NullReferenceException. DN 4.0
Viacheslav Smityukh

Interesante. Cuando me meto ((object)null).ToString()dentro de un try/catchme sale NullReferenceExceptiontambién.
leppie

3
@Ieppie, excepto que tampoco arroja AccessViolation (¿por qué lo haría?) - NullReferenceException aquí.
jeroenh

11

La primera parte de su código se trata así en String.Concat,

que es lo que llama el compilador de C # cuando agrega cadenas. " abc" + nullse traduce a String.Concat("abc", null),

e internamente, ese método reemplaza nullcon String.Empty. Por eso, su primera parte del código no arroja ninguna excepción. es como

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

Y en la segunda parte de su código arroja una excepción porque 'nulo' no es un objeto, la palabra clave nula es un literal que representa una referencia nula , una que no hace referencia a ningún objeto. nulo es el valor predeterminado de las variables de tipo de referencia.

Y ' ToString()' es un método que puede ser llamado por una instancia de un objeto pero no por un literal.


3
String.Emptyno es igual a null. Simplemente se trata de la misma manera en algunos métodos como String.Concat. Por ejemplo, si tiene una variable de cadena establecida en null, C # no la reemplazará String.Emptysi intenta llamar a un método.
Botz3000

3
Casi. nullno es tratado como String.Emptypor C #, solo es tratado así String.Concat, que es lo que llama el compilador de C # cuando agrega cadenas. "abc" + nullse traduce String.Concat("abc", null)e internamente ese método reemplaza nullcon String.Empty. La segunda parte es completamente correcta.
Botz3000

9

En el marco COM que precedió a .net, era necesario que cualquier rutina que recibiera una cadena lo liberara cuando terminara. Debido a que era muy común que las cadenas vacías se pasaran dentro y fuera de las rutinas, y porque el intento de "liberar" un puntero nulo se definía como una operación legítima de no hacer nada, Microsoft decidió que un puntero de cadena nula representara una cadena vacía.

Para permitir cierta compatibilidad con COM, muchas rutinas en .net interpretarán un objeto nulo como una representación legal como una cadena vacía. Con un par de cambios leves .net y sus idiomas (lo que permite que los miembros de la instancia indiquen "no invocar como virtual"), Microsoft podría haber hecho que los nullobjetos de tipo declarado String se comporten aún más como cadenas vacías. Si Microsoft hubiera hecho eso, también habría tenido que hacer que el Nullable<T>trabajo fuera algo diferente (para permitir, Nullable<String>algo que deberían haber hecho en mi humilde opinión de todos modos) y / o definir un NullableStringtipo que sería en su mayoría intercambiable String, pero que no consideraría un nullcomo una cadena vacía válida.

Tal como están las cosas, hay algunos contextos en los que a nullse considerará una cadena vacía legítima y otros en los que no. No es una situación terriblemente útil, pero una que los programadores deben tener en cuenta. En general, las expresiones del formulario stringValue.someMemberfallarán si stringValuees así null, pero la mayoría de los métodos y operadores de marco que aceptan cadenas como parámetros se considerarán nullcomo una cadena vacía.


5

'+'es un operador infijo. Como cualquier operador, realmente está llamando a un método. Podrías imaginar una versión no infija"wow".Plus(null) == "wow"

El implementador ha decidido algo como esto ...

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

Entonces ... tu ejemplo se convierte

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

que es lo mismo que

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

En ningún momento nulo se convierte en una cadena. Entonces null.ToString()no es diferente eso null.VoteMyAnswer(). ;)


Realmente, es más como var bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");. Las sobrecargas de los operadores son métodos estáticos en su esencia: msdn.microsoft.com/en-us/library/s53ehcz3(v=VS.71).aspx Si no lo fueran, var bob = null + "abc";o especialmente string bob = null + null;no serían válidos.
Ben Mosher

Tienes razón, sentí que sería demasiado confuso si lo explicara de esa manera.
Nigel Thorne

3

Supongo que porque es un literal que no se refiere a ningún objeto. ToString()necesita un object.


3

Alguien dijo en este hilo de discusión que no se puede hacer una cadena de la nada. (que es una bonita frase como creo). Pero sí, puedes :-), como muestra el siguiente ejemplo:

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

funciona bien y no arroja una excepción en absoluto. La única diferencia es que debe convertir uno de los valores nulos en una cadena: si elimina la conversión (cadena) , el ejemplo aún se compila, pero arroja una excepción en tiempo de ejecución: "Operador '+' es ambiguo en operandos de escriba '<nulo>' y '<nulo>' ".

Nota: en el ejemplo de código anterior, el valor de x no es nulo como podría esperarse, en realidad es una cadena vacía después de haber convertido uno de los operandos en una cadena.


Otro hecho interesante es que en C # / .NET la forma en que nullse trata no siempre es la misma si se consideran diferentes tipos de datos. Por ejemplo:

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

Con respecto a la primera línea del fragmento de código: si xes una variable entera anulable (es decir, que int?contiene un valor) 1, está obteniendo el resultado <null>. Si es una cadena (como se muestra en el comentario) con valor "1", entonces está "1"regresando en lugar de hacerlo <null>.

Nota: también es interesante: si está utilizando var x = 1;la primera línea, obtendrá un error de tiempo de ejecución. ¿Por qué? Debido a que la asignación convertirá la variable xen el tipo de datos int, que no es anulable. El compilador no asume int?aquí, y por lo tanto falla en la segunda línea donde nullse agrega.


2

Agregar nulla una cadena simplemente se ignora. null(en su segundo ejemplo) no es una instancia de ningún objeto, por lo que ni siquiera tiene un ToString()método. Es solo un literal.


1

Porque no hay diferencia entre string.Emptyy nullcuando concatene cadenas. Puede pasar nulo string.Formattambién. Pero está intentando llamar a un método null, que siempre daría como resultado NullReferenceExceptionay, por lo tanto, genera un error de compilación.
Si por alguna razón realmente desea hacerlo, puede escribir un método de extensión, que verifica nully luego regresa string.Empty. Pero una extensión como esa solo debería usarse cuando sea absolutamente necesario (en mi opinión).


0

Como general: puede o no ser válido aceptar nulo como parámetro dependiendo de la especificación, pero siempre es inválido llamar a un método en nulo.


Ese es otro tema por el cual los operandos del operador + pueden ser nulos en el caso de cadenas. Esto es algo de VB (lo siento chicos) para facilitar la vida de los programadores, o suponiendo que el programador no pueda lidiar con los valores nulos. Estoy completamente en desacuerdo con esta especificación. 'desconocido' + 'cualquier cosa' debería seguir siendo 'desconocido' ...

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.