¿Cómo es null + true una cadena?


Dado que true no es un tipo de cadena, ¿cómo es null + true una cadena ?

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

¿Cuál es la razón detrás de esto?

Author: Brooks Moses, 2010-12-17

7 answers

Por extraño que parezca, simplemente sigue las reglas de la especificación del lenguaje C#.

De la sección 7.3.4:

Una operación de la forma x op y, donde op es un operador binario sobrecargable, x es una expresión de tipo X, y y es una expresión de tipo Y, se procesa de la siguiente manera:

  • Se determina el conjunto de operadores candidatos definidos por el usuario proporcionados por X e Y para el operador de operación op(x, y). El conjunto consiste en la unión de la los operadores candidatos proporcionados por X y los operadores candidatos proporcionados por Y, cada uno determinado utilizando las reglas de §7.3.5. Si X e Y son del mismo tipo, o si X e Y se derivan de un tipo base común, entonces los operadores candidatos compartidos solo ocurren en el conjunto combinado una vez.
  • Si el conjunto de operadores candidatos definidos por el usuario no está vacío, entonces esto se convierte en el conjunto de operadores candidatos para la operación. De lo contrario, las implementaciones op del operador binario predefinidas, incluyendo su formas, se convierten en el conjunto de operadores candidatos para la operación. Las implementaciones predefinidas de un operador dado se especifican en la descripción del operador (§7.8 a §7.12).
  • Las reglas de resolución de sobrecarga de §7.5.3 se aplican al conjunto de operadores candidatos para seleccionar el mejor operador con respecto a la lista de argumentos (x, y), y este operador se convierte en el resultado del proceso de resolución de sobrecarga. Si la resolución de sobrecarga no selecciona un solo mejor operador, un se produce un error de tiempo de enlace.

Por lo tanto, vamos a caminar a través de esto a su vez.

X es el tipo nulo aquí - o no es un tipo en absoluto, si quieres pensarlo de esa manera. No está proporcionando ningún candidato. Y es bool, que no proporciona ningún operador + definido por el usuario. Así que el primer paso no encuentra operadores definidos por el usuario.

El compilador luego pasa al segundo punto de viñeta, mirando a través del operador binario predefinido + implementaciones y su levantado forma. Estos se enumeran en la sección 7.8.4 de la especificación.

Si miras a través de esos operadores predefinidos, el solo que es aplicable es string operator +(string x, object y). Así que el conjunto candidato tiene una sola entrada. Eso hace que el punto final sea muy simple... la resolución de sobrecarga selecciona ese operador, dando un tipo de expresión general de string.

Un punto interesante es que esto ocurrirá incluso si hay otros operadores definidos por el usuario disponibles en tipos no mencionados. Para ejemplo:

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

Eso está bien, pero no se usa para un literal nulo, porque el compilador no sabe buscar en Foo. Solo sabe considerar string porque es un operador predefinido que aparece explícitamente en la especificación. (De hecho, es no un operador definido por el tipo de cadena... 1) Esto significa que esto fallará al compilar:

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

Otros tipos de segundo operando usarán algunos otros operadores, por supuesto:

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 Tú puede que se esté preguntando por qué no hay un operador string+. Es una pregunta razonable, y solo estoy adivinando en la respuesta, pero considere esta expresión:

string x = a + b + c + d;

Si string no tuviera una carcasa especial en el compilador de C#, esto terminaría igual de efectivo:

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

Por lo que se crean dos cadenas intermedias innecesarias. Sin embargo, debido a que hay un soporte especial dentro del compilador, es en realidad capaz de compilar lo anterior como:

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

Que puede crear una sola cadena de exactamente la longitud correcta, copiando todos los datos exactamente una vez. Agradable.

 149
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-17 17:22:54

La razón es porque una vez que se introduce el + entonces entran en juego las reglas vinculantes del operador C#. Considerará el conjunto de operadores + disponibles y seleccionará la mejor sobrecarga. Uno de esos operadores es el siguiente

string operator +(string x, object y)

Esta sobrecarga es compatible con los tipos de argumento de la expresión null + true. Por lo tanto, se selecciona como el operador y se evalúa como esencialmente ((string)null) + true que evalúa al valor "True".

Sección 7.7.4 de la especificación del lenguaje C # contiene los detalles en torno a esta resolución .

 44
Author: JaredPar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-17 17:16:28

El compilador sale a la caza de un operador+() que puede tomar un argumento null primero. Ninguno de los tipos de valor estándar califica, null no es un valor válido para ellos. La única coincidencia es el Sistema.Cadena.operador + (), no hay ambigüedad.

El segundo argumento de ese operador es también una cadena. Eso va kapooey, no puede convertir implícitamente bool a cadena.

 11
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-17 17:13:04

Curiosamente, usando Reflector para inspeccionar lo que se genera, el siguiente código:

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

Se transforma en esto por el compilador:

Console.WriteLine(true);

El razonamiento detrás de esta "optimización" es un poco extraño, debo decir, y no rima con la selección del operador que esperaría.

También, el siguiente código:

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

Se transforma en

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

Donde string b = true; en realidad no es aceptado por el compilador.

 10
Author: Peter Lillevold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-17 17:33:09

null se lanzará a cadena nula, y hay un convertidor implícito de bool a cadena por lo que el true se lanzará a cadena y luego, se aplicará el operador +: es como: string str = "" + true.toString ();

Si usted comprobarlo con Ildasm:

string str = null + true;

Es como abajo:

.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
 8
Author: Saeed Amiri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-18 06:02:36
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

Loco?? No, debe haber una razón detrás.

Alguien llame Eric Lippert...

 5
Author: decyclone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-17 17:12:58

La razón de esto es la conveniencia (concatenar cadenas es una tarea común).

Como dijo BoltClock, el operador '+' se define en tipos numéricos, cadenas, y también se puede definir para nuestros propios tipos (sobrecarga del operador).

Si no hay un operador '+' sobrecargado en los tipos del argumento y no son tipos numéricos, el compilador usa por defecto la concatenación de cadenas.

El compilador inserta una llamada a String.Concat(...) cuando concatenas usando'+', y la implementación de Concat llama toString en cada objeto pasado en él.

 5
Author: quentin-starin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-17 17:13:55