可空类型到底什么时候抛出异常?
When exactly do nullable types throw exceptions?
考虑以下代码:
int? x = null;
Console.Write ("Hashcode: ");
Console.WriteLine(x.GetHashCode());
Console.Write("Type: ");
Console.WriteLine(x.GetType());
执行时,它写入 Hashcode 是 0
,但在尝试确定 x
的类型时失败 NullReferenceException
。
我知道在可空类型上调用的方法实际上是在基础值上调用的,所以我预计程序会在 x.GetHashCode()
.
期间失败
那么,这两种方法之间的根本区别是什么?为什么第一种方法不会失败?
似乎 GetHashCode 进行了空检查。 (使用 JetBrains 查看防御)
public override int GetHashCode()
{
if (!this.hasValue)
return 0;
return this.value.GetHashCode();
}
Nullable<T>.GetHashCode()
的实现如下:
public override int GetHashCode()
{
if (!this.HasValue)
{
return 0;
}
return this.value.GetHashCode();
}
因此,当值为 null 时,它将始终为您提供 0
。
x.GetType()
与 null.GetType()
相同,后者将抛出 Object reference not set to an instance of an object
这是因为 int? x = null;
本质上创建了一个值类型 System.Nullable<int>
的实例,具有 "inner" null
值(您可以通过 .HasVaue
属性)。当GetHashCode
被调用时,overrideNullable<int>.GetHashCode
是候选方法(因为方法是virtual),现在我们有一个Nullable<int>
的实例,执行它的实例方法,完美。
在调用GetType
时,方法是非virtual的,所以Nullable<int>
的实例先装箱到System.Object
,根据the document,装箱后的值是 null
,因此 NullReferenceException
.
澄清Danny Chen的正确答案:
Nullable<T>
是值类型。值类型由一个 bool 和一个 T 组成,bool 表示 null(false 表示 null)和 T,值。
- 与所有其他值类型不同,可为 null 的类型不会装箱到装箱
Nullable<T>
。他们装箱到装箱 T
或空引用。
- 由值类型
S
实现的方法的实现就像它具有不可见的 ref S
参数一样;这就是 this
的传递方式。
- 引用类型
C
实现的方法就像有一个不可见的 C
参数一样实现;这就是 this
的传递方式。
- 有趣的情况是在引用基 class 中定义的虚方法,并被从基 class 继承的结构覆盖。
现在你有足够的信息来推断会发生什么。 GetHashCode
是虚拟的 并被 Nullable<T>
覆盖,所以当你调用它时,你调用它就好像 [=15] 有一个不可见的 ref Nullable<T>
参数=].没有拳击发生。
GetType
不是虚拟的,因此不能被覆盖并且在 object
上定义。因此它期望 this
的 object
,当在 Nullable<T>
上调用时,接收器必须装箱,因此可以装箱为空,因此可以抛出。
如果您调用 ((object)x).GetHashCode()
,您会看到异常。
考虑以下代码:
int? x = null;
Console.Write ("Hashcode: ");
Console.WriteLine(x.GetHashCode());
Console.Write("Type: ");
Console.WriteLine(x.GetType());
执行时,它写入 Hashcode 是 0
,但在尝试确定 x
的类型时失败 NullReferenceException
。
我知道在可空类型上调用的方法实际上是在基础值上调用的,所以我预计程序会在 x.GetHashCode()
.
那么,这两种方法之间的根本区别是什么?为什么第一种方法不会失败?
似乎 GetHashCode 进行了空检查。 (使用 JetBrains 查看防御)
public override int GetHashCode()
{
if (!this.hasValue)
return 0;
return this.value.GetHashCode();
}
Nullable<T>.GetHashCode()
的实现如下:
public override int GetHashCode()
{
if (!this.HasValue)
{
return 0;
}
return this.value.GetHashCode();
}
因此,当值为 null 时,它将始终为您提供 0
。
x.GetType()
与 null.GetType()
相同,后者将抛出 Object reference not set to an instance of an object
这是因为 int? x = null;
本质上创建了一个值类型 System.Nullable<int>
的实例,具有 "inner" null
值(您可以通过 .HasVaue
属性)。当GetHashCode
被调用时,overrideNullable<int>.GetHashCode
是候选方法(因为方法是virtual),现在我们有一个Nullable<int>
的实例,执行它的实例方法,完美。
在调用GetType
时,方法是非virtual的,所以Nullable<int>
的实例先装箱到System.Object
,根据the document,装箱后的值是 null
,因此 NullReferenceException
.
澄清Danny Chen的正确答案:
Nullable<T>
是值类型。值类型由一个 bool 和一个 T 组成,bool 表示 null(false 表示 null)和 T,值。- 与所有其他值类型不同,可为 null 的类型不会装箱到装箱
Nullable<T>
。他们装箱到装箱T
或空引用。 - 由值类型
S
实现的方法的实现就像它具有不可见的ref S
参数一样;这就是this
的传递方式。 - 引用类型
C
实现的方法就像有一个不可见的C
参数一样实现;这就是this
的传递方式。 - 有趣的情况是在引用基 class 中定义的虚方法,并被从基 class 继承的结构覆盖。
现在你有足够的信息来推断会发生什么。 GetHashCode
是虚拟的 并被 Nullable<T>
覆盖,所以当你调用它时,你调用它就好像 [=15] 有一个不可见的 ref Nullable<T>
参数=].没有拳击发生。
GetType
不是虚拟的,因此不能被覆盖并且在 object
上定义。因此它期望 this
的 object
,当在 Nullable<T>
上调用时,接收器必须装箱,因此可以装箱为空,因此可以抛出。
如果您调用 ((object)x).GetHashCode()
,您会看到异常。