为什么n.GetHashCode()工作,但n.GetType()抛出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());
直观地,我理解为什么GetType()方法会抛出exception。 实例n是null,这可以解释这一点,但是,为什么在使用n.GetHashCode()和ToString()时出于同样的原因我不会得到exception?
谢谢您的帮助,
约翰。
GetHashCode()
是在Nullable
重写的虚方法:当它在Nullable
值上调用时,使用Nullable
实现,没有任何装箱。
GetType()
不是一个虚方法,这意味着当它被调用时,该值首先被加框…并且装箱“null”可空值导致空引用 – 因此是exception。 我们可以从IL看到这个:
static void Main() { bool? x = null; Type t = x.GetType(); }
编译为:
.method private hidebysig static void Main() cil managed { .entrypoint .maxstack 1 .locals init ( [0] valuetype [mscorlib]System.Nullable`1 nullable, [1] class [mscorlib]System.Type 'type') L_0000: nop L_0001: ldloca.s nullable L_0003: initobj [mscorlib]System.Nullable`1 L_0009: ldloc.0 L_000a: box [mscorlib]System.Nullable`1 L_000f: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType() L_0014: stloc.1 L_0015: ret }
这里重要的一点是L_000a: callvirt
中callvirt指令之前的callvirt
指令。
现在将它与调用GetHashCode
的等效代码进行比较:
static void Main() { bool? x = null; int hash = x.GetHashCode(); }
编译为:
.method private hidebysig static void Main() cil managed { .entrypoint .maxstack 1 .locals init ( [0] valuetype [mscorlib]System.Nullable`1 nullable, [1] int32 num) L_0000: nop L_0001: ldloca.s nullable L_0003: initobj [mscorlib]System.Nullable`1 L_0009: ldloca.s nullable L_000b: constrained [mscorlib]System.Nullable`1 L_0011: callvirt instance int32 [mscorlib]System.Object::GetHashCode() L_0016: stloc.1 L_0017: ret }
这次我们在callvirt
之前有一个constrained
指令/前缀,这实质上意味着“当你调用虚方法时你不需要callvirt
”。 从OpCodes.Constrained
文档:
受约束前缀旨在允许以统一的方式进行callvirt指令,而与thisType是值类型还是引用类型无关。
(请点击链接获取更多信息。)
请注意,可以为空的值类型装箱的方式也意味着即使对于非空值,也不会获得Nullable
。 例如考虑:
int? x = 10; Type t = x.GetType(); Console.WriteLine(t == typeof(int?)); // Prints False Console.WriteLine(t == typeof(int)); // Prints True
所以你得到的类型是涉及的非可空类型。 对object.GetType()
调用永远不会返回Nullable
类型。