¿Por qué el compilador me permite convertir un null a un tipo específico en C#?


Considere este código:

var str = (string)null;

Cuando escriba el código este es mi IL código:

IL_0001:  ldnull

Y IL tiene cualquier operador de reparto pero:

var test = (string) new Object();

El código IL es:

IL_0008:  castclass  [mscorlib]System.String

Así que se ignoró el lanzamiento de null a string.

¿Por qué el compilador me permite convertir un null a un tipo específico?

Author: Majid, 2014-01-05

9 answers

En IL en este nivel, null es solo null. El compilador sabía que era null porque eso es lo que escribiste, como tal, el compilador no necesita llamar al operador cast en absoluto. Lanzar null a un objeto solo producirá null.

Así que esta es una "optimización" en tiempo de compilación o simplificación si se quiere.

Dado que esto es legal, para lanzar null a otro tipo de objeto, no hay una advertencia ni un error reportado de esto.

Tenga en cuenta que aparentemente el compilador no hará esto incluso aunque pueda ser capaz de verificar que el valor que se está emitiendo está garantizado que es null, si no es un literal.

Su ejemplo:

void Main()
{
    var s = (string)null;
    GC.KeepAlive(s);
}

IL:

IL_0000:  ldnull      
IL_0001:  stloc.0     // s
IL_0002:  ldloc.0     // s
IL_0003:  call        System.GC.KeepAlive

(He añadido la llamada a GC.KeepAlive para evitar que el compilador deje caer toda la variable debido a que no se utiliza en ninguna parte.)

Si primero introduzco el null en un objeto, sin posibilidad de que cambie: {[14]]}

void Main()
{
    object o = null;
    var s = (string)o;
    GC.KeepAlive(s);
}

IL:

IL_0000:  ldnull      
IL_0001:  stloc.0     // o
IL_0002:  ldloc.0     // o
IL_0003:  castclass   System.String
IL_0008:  stloc.1     // s
IL_0009:  ldloc.1     // s
IL_000A:  call        System.GC.KeepAlive
 48
Author: Lasse Vågsæther Karlsen,
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
2014-01-05 22:27:34

En Java hay al menos un caso en el que necesitas convertir un null a algún tipo, y es cuando usas métodos sobrecargados para decirle al compilador qué método quieres ejecutar (supongo que este es el caso en C# también). Dado que un null es 0 (o cualquier puntero null representa) no importa qué tipo es, no verá ninguna diferencia en el código compilado (aparte de qué método fue llamado).

 20
Author: Njol,
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
2014-01-08 16:54:00

Porque la especificación lo dice. Véanse §6.1.5, §6.2 y §7.7.6 de la norma C# 5. Para citar solo las partes relevantes:

§7.7.6 Expresiones de reparto

Un cast-la expresión de la forma (T)E, donde T es a tipo y E es a unario-expresión, realiza una conversión explícita (§6.2) del valor de E para escribir T. [... El resultado es el valor producido por la conversión explícita.

§6.2 Conversiones explícitas

Las siguientes conversiones se clasifican como conversiones explícitas:

  • Todas las conversiones implícitas.

§6.1.5 Conversiones de referencia implícitas

Las conversiones de referencia implícitas son:

  • Del literal nulo a cualquier tipo de referencia.
 14
Author: Anton Tykhyy,
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
2014-01-06 06:30:25

El casting de un null es perfectamente válido - a veces es necesario cuando se pasan argumentos a métodos sobrecargados para informar al compilador qué método se está invocando.

Vea la siguiente pregunta relacionada:

Casting null como un objeto?

 9
Author: ColinE,
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
2017-05-23 12:29:53

El objetivo de la conversión es definir str como un tipo de cadena, por lo que se trata menos de si se puede convertir un null como un Tipo y más de definir el tipo de la variable.

 5
Author: Rory,
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
2014-01-05 12:06:38

No todo lo que parece (SomeType)expression es realmente un molde, en C#. A veces la expresión necesita adquirir un tipo que no tenía ya. Algunos otros ejemplos:

var a = (short)42;
var b = (Func<int, int>)(i => i + 1);
var c = (IConvertible)"Hello";

En cada uno de estos casos, uno también podría haber escrito el tipo a la izquierda, en lugar de var, si uno prefería eso. Pero este no es siempre el caso cuando la expresión es parte de una expresión más grande, digamos:

CallOverloadedMethod((short)42); // CallOverloadedMethod has another overload that we don't want
var y = b ? x : (IConvertible)"Hello"; // the ?: operator might not be able to figure out the type without this hint

En su ejemplo el literal null no tiene ningún tipo en sí mismo. En algunos casos, como cuando se elige entre muchas sobrecargas, como cuando se usa el operador ternario ?:, o cuando se declara una variable con sintaxis var, es necesario tener una expresión que todavía es null pero también lleva un tipo.

Tenga en cuenta que las sobrecargas también incluyen operadores, por ejemplo en:

public static bool operator ==(Giraffe g1, Giraffe g2)
{
  if (g1 == (object)null && g2 != (object)null
    || g1 != (object)null && g2 == (object)null)
  {
    return false;
  }

  // rest of operator body here ...
}

La sintaxis (object)null se utiliza para garantizar que la sobrecarga definida por el usuario de == no se llame recursivamente.

 5
Author: Jeppe Stig Nielsen,
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
2014-01-05 21:40:11

Su sintaxis es correcta y no hay limitaciones de especificación en c#. Estas son reglas de especificación:

Las conversiones de referencia implícitas son:

  • De cualquier tipo de referencia a objeto y dinámico.

  • De cualquier clase-tipo S a cualquier clase-tipo T, siempre que S se derive de T.

  • De cualquier tipo de clase S a cualquier tipo de interfaz T, siempre que S implemente T.

  • Desde cualquier interfaz-tipo S a cualquier interfaz-tipo T, siempre que S sea derivado de T.

  • De un array-type S con un elemento type SE a un array-type T con un tipo de elemento TE, siempre que se cumplan todas las siguientes condiciones: o S y T difieren solo en el tipo de elemento. En otras palabras, S y T tienen lo mismo número de dimensiones. o Tanto SE como TE son tipos de referencia. o An la conversión de referencia implícita existe de SE a TE.

  • De cualquier tipo de matriz al Sistema.Array y las interfaces implementar.

  • De una matriz unidimensional tipo S [] a Sistema.Colecciones.Generico.IList y sus interfaces base, siempre que hay una identidad implícita o conversión de referencia de S a T.

  • De cualquier tipo de delegado al Sistema.Delegar y las interfaces it implementar.

  • Del literal nulo a cualquier tipo de referencia.

  • De cualquier tipo de referencia a un tipo de referencia T si tiene una conversión de identidad o referencia a un tipo de referencia T0 y T0 tiene un conversión de identidad a T.

  • De cualquier tipo de referencia a una interfaz o tipo de delegado T si tiene una conversión implícita de identidad o referencia a una interfaz o el tipo de delegado T0 y T0 es varianza convertible (§13.1.3.2) a T.

  • Conversiones implícitas que involucran parámetros de tipo que se sabe que son tipos de referencia. Consulte §6.1.10 para obtener más detalles sobre las conversiones implícitas involucrar escriba parámetros. Las conversiones de referencia implícitas son esas conversiones entre tipos de referencia que se puede demostrar que siempre tener éxito, y por lo tanto no requieren comprobaciones en tiempo de ejecución. Referencia conversiones, implícitas o explícitas, nunca cambian el referencial identidad del objeto que se está convirtiendo. En otras palabras, mientras que un conversión de referencia puede cambiar el tipo de la referencia, nunca cambia el tipo o valor del objeto al que se hace referencia.

 2
Author: Pooya Yazdani,
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
2014-01-05 16:42:44

Supongamos que el compilador devuelve una advertencia para var str = (string)null;. Entonces, ¿debería alertarse a esta línea?: var str = SomeFunctionThatReturnsNull() Mientras se usa el compilador var debe saber qué tipo se supone que debe inicializar en tiempo de compilación, y también no importa si se va a poner un valor null en él siempre y cuando se declare como un tipo nullable. No es ninguna sorpresa ver que el compilador no está llamando a Cast en caso nulo porque no hay nada para ser cast.

 1
Author: Behrad Farsi,
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
2014-01-05 12:11:04

Creo que deberías leer especificación.Puede emitir un valor nulo a todos los que desee. Véase:

• Del literal nulo a cualquier tipo de referencia.

Justo antes de usar el valor que comprueba para null.

 0
Author: Mohammad Zargarani,
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
2014-01-05 14:53:54