GetHashCode()
es un método virtual se reemplaza en Nullable<T>
: cuando se llama a un valor Nullable<T>
, se utiliza la aplicación Nullable<T>
, sin ningún tipo de boxeo.
GetType()
no es un método virtual, lo que significa que cuando se llama, el valor se encajona primera ... y boxeo un resultado "nulo" anulables de valor en una referencia nula - de ahí la excepción. Podemos ver esto desde el IL:
static void Main()
{
bool? x = null;
Type t = x.GetType();
}
se compila a:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<bool> nullable,
[1] class [mscorlib]System.Type 'type')
L_0000: nop
L_0001: ldloca.s nullable
L_0003: initobj [mscorlib]System.Nullable`1<bool>
L_0009: ldloc.0
L_000a: box [mscorlib]System.Nullable`1<bool>
L_000f: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
L_0014: stloc.1
L_0015: ret
}
El bit importante aquí es L_000a: box
la instrucción antes de la instrucción callvirt
en L_000f.
Ahora compare eso con el código equivalente llamando GetHashCode
:
static void Main()
{
bool? x = null;
int hash = x.GetHashCode();
}
compila a:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<bool> nullable,
[1] int32 num)
L_0000: nop
L_0001: ldloca.s nullable
L_0003: initobj [mscorlib]System.Nullable`1<bool>
L_0009: ldloca.s nullable
L_000b: constrained [mscorlib]System.Nullable`1<bool>
L_0011: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
L_0016: stloc.1
L_0017: ret
}
Esta vez tenemos un constrained
instrucción/prefijo antes callvirt
, lo que significa esencialmente "Usted don' Necesito boxear cuando llamas al método virtual ". Desde el OpCodes.Constrained
documentación:
El prefijo restringido está diseñado para permitir a las instrucciones callvirt hacerse de una manera uniforme, independiente de si thisType es un tipo de valor o de un tipo de referencia.
(Siga el enlace para más información.)
Tenga en cuenta que la forma de boxeo de los tipos de valor anulables trabajo también significa que incluso para un no nulo valor, no se va a Nullable<T>
. Por ejemplo, considere:
int? x = 10;
Type t = x.GetType();
Console.WriteLine(t == typeof(int?)); // Prints False
Console.WriteLine(t == typeof(int)); // Prints True
Así que el tipo de levantarse es el tipo no anulable involucrados. Una llamada a object.GetType()
será nunca devolverá un tipo Nullable<T>
.
mira esto: http://stackoverflow.com/questions/194484/whats-the-strangest-corner-case-youve-seen-in-c-or-net/194671#194671 – mookid8000
mookid, gran hilo. Gracias por señalarlo. – Hex440bx