可空类型到底什么时候抛出异常?

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 上定义。因此它期望 thisobject,当在 Nullable<T> 上调用时,接收器必须装箱,因此可以装箱为空,因此可以抛出。

如果您调用 ((object)x).GetHashCode(),您会看到异常。