2009-08-12 12 views
9

¿Tiene algún tipo el tipo null? ¿Cómo se representa un valor nulo internamente? ¿Qué está sucediendo en el siguiente código?En .Net/C#, ¿es nulo fuertemente tipado?

void Foo(string bar) {...} 
void Foo(object bar) {...} 

Foo((string)null); 

Editar: Las respuestas han sido hasta ahora demasiado inespecíficos y de alto nivel. Entiendo que un objeto de tipo de referencia consiste en un puntero en la pila que apunta a una ubicación en el montón que contiene un índice de bloque de sincronización, un manejador de tipo y los campos del objeto. Cuando establezco una instancia de un objeto en null, ¿hacia dónde apunta exactamente el puntero en la pila? Y en el fragmento de código, ¿el molde simplemente se usa por el compilador de C# para decidir a qué sobrecarga llamar, y no hay realmente lanzando null?

Busco una respuesta en profundidad por alguien que entienda el funcionamiento interno CLR.

Respuesta

16

El molde a string en su muestra de código no da el tipo null, ya que null no puede tener un tipo propio. Si quieres una prueba de esto, a continuación, ejecutar el siguiente código donde se puede ver que null es siempre igual a sí mismo, con independencia de lo que el tipo de variable se asigna a es:

string s = null; 
IPAddress i = null; 
Console.WriteLine(object.Equals(s, i)); // prints "True" 
Console.WriteLine(object.ReferenceEquals(s, i)); // prints "True" 

Lo que el elenco hace es decirle el compilador que sobrecarga para elegir.Como null no tiene un tipo que no sabe si elegir la sobrecarga que toma un object o una string como el valor podría ser interpretado como tampoco. Así que lo está ayudando diciendo "Aquí hay un valor nulo que debe tratarse como si fuera una cadena".


Si quiere ver lo que está sucediendo debajo, mire la IL desde su código. El bit relevante para la llamada al método es algo así como lo siguiente en IL textual (dependiendo de su espacio de nombres y nombre de clase, etc.):

ldnull 
call void ConsoleApplication1.Program::Foo(string) 

Así que todo lo que está sucediendo es que un nulo se carga en la pila, y entonces esto es consumido por la sobrecarga que toma la cadena, ya que la resolución de sobrecarga se realiza en tiempo de compilación, por lo que el método para llamar se procesa en la IL.

Si usted quiere ver lo que ldnull hace, y por qué es diferente a simplemente usando algo como ldc.i4.0 para cargar un cero en la pila y luego ver this answer (si no desea seguir el enlace, la razón es que es un cero independiente del tamaño que de otro modo no existiría en el CLR).

+0

Entonces, ¿qué exactamente estará en la pila cuando me refiero a 'null'? –

+0

@Matt: editado para agregar más detalles de bajo nivel. ¿Es esto suficiente? –

+0

@Greg: Gracias - Estaba ocupado en el trabajo, así que no tuve tiempo de revisar IL y la especificación de ECMA. Esto ha arañado mi picazón intelectual. –

2

Llamará a Foo (barra de cadenas).

Puedes lanzar bien nula.

+0

Obviamente, por favor vea mi edición. –

1

la palabra clave NULL es un literal que representa una referencia nula, uno que no se refiere a cualquier objeto. null es el valor predeterminado de las variables de tipo de referencia. De MSDN. Entonces, el valor predeterminado de String en tu ejemplo.

// A null string is not the same as an empty string. 
string s = null; 
string t = String.Empty; // Logically the same as "" 

Lo que está haciendo es equivalente a usar por defecto a lo anterior:

int equal = string.Compare((string)null, default(string)); 
2

Al configurar un objeto de referencia a null, el puntero (detrás) apunta a un lugar especial en la memoria que se designa null. Así que cuando lanzas a null, realmente creas un puntero del Tipo específico que apunta a null.

Como se ha demostrado por otros (1)(2), esto no parece ser el caso porque el compilador resuelve la sobrecarga que emite la IL acuerdo con el null molde específico.

sé que las variables de referencia tienen un tipo asociado con ellos. Pensé que esto estaría detrás de este casting null, pero estaba equivocado.

El ldnull opcode empuja una referencia nula (tipo O) en la pila. Y, incluso si esta referencia nula tiene un tipo asociado, el call acepta esta referencia como un argumento válido de string. Al menos, esta es mi comprensión de eso. Si alguien tiene alguna corrección a esto, no dude en corregirme.

1

NULL es un Marcador, no tiene un tipo de datos. Cuando asigna .NULL a un campo o variable, el valor cambia a NULO, pero el tipo de datos del campo o variable no cambia.

La razón para asignar el tipo de cadena a nulo en su ejemplo creo que es para mostrar que se usará el método de bruja (en este caso "void (barra de cadenas) {...}" porque este es el método acepta cadenas como argumentos)

Si hubiera llamado Foo nulo ((objeto).); el otro método se habría utilizado. ("void Foo (barra de objetos) {...}")

6

null no tiene el tipo, y "Y en el fragmento de código, el molde simplemente es utilizado por el compilador C# para decidir a qué sobrecarga llamar, y no hay realmente ningún lanzamiento de nulo? " es exactamente lo que está pasando. Miremos el IL generado.

IL_0001: ldnull 
    IL_0002: call  void ConsoleApplication1.Program::Foo(string) 

Observe que ldnull carga nulo en la pila. Es simplemente nulo, no nulo como una cadena u otra cosa. Lo que importa es la segunda línea, donde IL llama explícitamente a la sobrecarga que recibe una cadena. Esto es lo que sucede si se llama, echando a objetar:

IL_0001: ldnull 
    IL_0002: call  void ConsoleApplication1.Program::Foo(object) 

Así que, sí, el reparto es un artefacto # C por lo que el compilador sabe qué es la sobrecarga de llamar.

1

El tipo del valor null se define por ECMA-334 como sigue:

11.2.7 El tipo nulo

La nula literal (§9.4.4.6) se evalúa como el valor nulo , que se utiliza para denotar una referencia no señala en cualquier objeto o matriz, o la ausencia de un valor. El tipo nulo tiene un valor único, que es el valor nulo. Por lo tanto una expresión cuyo tipo es el tipo nulo puede evaluar sólo al valor nulo. No hay forma de escribir explícitamente el tipo nulo y, por lo tanto, no hay forma de para usarlo en un tipo declarado.

El tipo nula es el tipo de parte inferior de la jerarquía de tipos - lo contrario de objeto; el tipo nulo se puede considerar como un subtipo de cada tipo que admite nulos, ya que el valor nulo se puede usar dondequiera que se produzca cualquier expresión que pueda nulos.

Esto crea una debilidad en el sistema de tipos, ya que significa que cualquier operación cuyo receptor sea de tipo anulable podría ser nula y la operación puede fallar en el tiempo de ejecución con una excepción. En un sistema de tipo fuerte, las operaciones suministradas por un tipo están garantizadas si el receptor es de ese tipo (es decir, no obtendría una excepción de puntero nulo, lo que realmente significa que el tipo nulo no implementa el método que usted tenga). invocado en él, aunque el valor nulo puede ser el resultado de la expresión del tipo que pensaste que estabas usando).

En su código, los métodos están sobrecargados y el tipo estático de la expresión que produce el argumento se usa para la resolución de sobrecarga. Debido a la conversión a cadena, la expresión tiene una cadena de tipo (el tipo nulo es un subtipo de cadena; esto es un lanzamiento mejorado tan seguro según el sistema de tipo C#). Como primera aproximación, el compilador selecciona la sobrecarga más específica visible, por lo que elige la versión de cadena de Foo en lugar de la versión del objeto.

Cuestiones relacionadas