我在教自己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()方法会抛出异常。实例n是null,这可以解释这个但是,为什么我在使用n.GetHashCode()和ToString()时出于同样的原因没有得到异常?
感谢您的帮助,
约翰。
答案 0 :(得分:14)
GetHashCode()
是在Nullable<T>
中重写的虚拟方法:当它在Nullable<T>
值上调用时,使用Nullable<T>
实现,而不进行任何限制。
GetType()
不是虚拟方法,这意味着当它被调用时,该值首先被装箱...并且装箱“null”可空值导致空引用 - 因此例外。我们可以从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<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
}
这里的重要位是L_000a:L_000f上box
指令之前的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<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
}
这次我们在constrained
之前有一个callvirt
指令/前缀,这实质上意味着“当你调用虚方法时你不需要打包”。来自OpCodes.Constrained
文档:
受约束的前缀旨在允许以统一的方式进行callvirt指令,而与thisType是值类型还是引用类型无关。
(请点击链接获取更多信息。)
请注意,可以为空的值类型装箱的方式也意味着即使对于非null 值,也不会获得Nullable<T>
。例如,考虑:
int? x = 10;
Type t = x.GetType();
Console.WriteLine(t == typeof(int?)); // Prints False
Console.WriteLine(t == typeof(int)); // Prints True
因此,您获得的类型是涉及的非可空类型。拨打object.GetType()
将从不返回Nullable<T>
类型。