为什么n.GetHashCode()工作但n.GetType()抛出异常?

Hex*_*0bx 9 c# exception

我在教自己C#(我还不太了解).在这个简单的例子中:

bool?          n = null;

Console.WriteLine("n               = {0}", n);
Console.WriteLine("n.ToString()    = {0}", n.ToString());
Console.WriteLine("n.GetHashCode() = {0}", n.GetHashCode());

// this next statement causes a run time exception

Console.WriteLine("n.GetType()     = {0}", n.GetType());
Run Code Online (Sandbox Code Playgroud)

直观地,我理解为什么GetType()方法会抛出异常.实例n是null,这可以解释这个但是,为什么我在使用n.GetHashCode()和ToString()时出于同样的原因没有得到异常?

谢谢您的帮助,

约翰.

Jon*_*eet 14

GetHashCode()是一个重写的虚方法Nullable<T>:当它被一个Nullable<T>值调用时,使用Nullable<T>实现,没有任何装箱.

GetType() 不是一个虚方法,这意味着当它被调用时,该值首先被加框...并且装箱"null"可空值导致空引用 - 因此是异常.我们可以从IL看到这个:

static void Main()
{
    bool? x = null;
    Type t = x.GetType();
}
Run Code Online (Sandbox Code Playgroud)

编译为:

.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 
}
Run Code Online (Sandbox Code Playgroud)

这里重要的位是L_000a:L_000f box指令之前的callvirt指令.

现在将其与等效代码调用进行比较GetHashCode:

static void Main()
{
    bool? x = null;
    int hash = x.GetHashCode();
}
Run Code Online (Sandbox Code Playgroud)

编译为:

.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 
}
Run Code Online (Sandbox Code Playgroud)

这次我们之前有一个constrained指令/前缀callvirt,这实际上意味着"当你调用虚方法时你不需要打包".从OpCodes.Constrained文档:

受约束的前缀被设计为允许以统一的方式进行callvirt指令,而与thisType是值类型还是引用类型无关.

(点击链接获取更多信息.)

请注意,可以为空的值类型装箱的方式也意味着即使对于非空值,也不会得到Nullable<T>.例如考虑:

int? x = 10;
Type t = x.GetType();
Console.WriteLine(t == typeof(int?)); // Prints False
Console.WriteLine(t == typeof(int)); // Prints True
Run Code Online (Sandbox Code Playgroud)

所以你得到的类型是涉及的非可空类型.的调用object.GetType()永远不会返回一个Nullable<T>类型.