C# 可空类型何时抛出异常?

C# 可空类型何时抛出异常?,c#,.net,nullreferenceexception,nullable,C#,.net,Nullreferenceexception,Nullable,考虑以下代码: int? x = null; Console.Write ("Hashcode: "); Console.WriteLine(x.GetHashCode()); Console.Write("Type: "); Console.WriteLine(x.GetType()); 当执行时,它写入Hashcode为0,但在尝试确定x的类型时,由于NullReferenceException而失败。 我知道在可空类型上调用的方法实际上是在底层值上调用的,所以我预计程序在x.GetHas

考虑以下代码:

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查看防御)


Nullable.GetHashCode()
的实现如下:

public override int GetHashCode()
{
    if (!this.HasValue)
    {
        return 0;
    }
    return this.value.GetHashCode();
}
因此,当值为null时,它将始终为您提供
0


x.GetType()
null.GetType()
相同,它将抛出未设置为对象实例的
对象引用

这是因为
int?x=零
基本上创建了值类型
System.Nullable
的一个实例,该实例带有一个“内部”
null
值(您可以通过
.HasVaue
属性进行检查)。当调用
GetHashCode
时,覆盖
Nullable.GetHashCode
是方法候选(因为该方法是虚拟的),现在我们有了一个
Nullable
的实例,并执行其实例方法,完美


调用
GetType
时,该方法是非虚拟的,因此
Nullable
的实例首先被装箱到
System.Object
,装箱值为
null
,因此
NullReferenceException
澄清Danny Chen的正确答案:

  • Nullable
    是一种值类型。值类型由一个bool和一个T组成,bool表示空值(false表示空值),T表示值
  • 与所有其他值类型不同,可空类型不会装箱到已装箱的
    nullable
    。它们将框设置为已装箱的
    T
    或空引用
  • 由值类型
    S
    实现的方法被实现为具有不可见的
    ref S
    参数;这就是传递此的方式
  • 由引用类型
    C
    实现的方法被实现为好像存在一个不可见的
    C
    参数;这就是传递此的方式
  • 有趣的情况是在引用基类中定义的虚拟方法,并由从基类继承的结构重写
现在你有足够的信息来推断发生了什么
GetHashCode
是虚拟的并且被
Nullable
覆盖,所以当您调用它时,您调用它就好像有一个不可见的
ref Nullable
参数用于
这个
。没有拳击比赛

GetType
不是虚拟的,因此不能被重写,并且是在
对象上定义的。因此,它期望
对象
用于
,当调用
可空
时,接收器必须装箱,因此可以装箱为空,因此可以抛出


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

我能找到的唯一区别是
GetHashCode
virtual
。。。是一个方便的小工具来帮助回答这类问题。我觉得奇怪的是,
Nullable
中的
GetType()
返回
System.Int32
,而不是
System.Nullable
。同样值得注意的是
int?x=null
Nullable x=newnullable(null)
的语法糖。因此,
x
是一个实际的对象,而不是空引用。引用源--不以任何方式显示正在处理的GetType,文档也没有对此进行详细说明-因此,GetHashCode是在Nullable上调用的,而不是在它包含的值上调用的?为什么会这样?为什么
x.GetType()
null.GetType()
相同,因为实际上您有
(struct).GetType()
Nullable
是一种结构、值类型。您是否也可以发布
GetType
的实现,以防它为答案添加更多细节?所有这些都与我们看到的行为相匹配,但它在哪里记录?您链接到的页面详细说明了装箱行为如何适用于
Nullable
,但没有说明
GetType()
将需要装箱操作。我猜我们需要找到关于继承方法如何处理值类型以查找相关片段的文档。@LasseVågsætherKarlsen这是因为
GetType
是在
object
上定义的,而不是
struct
,这会导致隐式装箱。您可以通过分配
int?x=1
然后调用
GetType()
。结果是
System.Int32
,而不是
Nullable
@LasseVågsætherKarlsen您可以阅读C语言规范第4.3节“装箱和拆箱”了解更多详细信息。如果我键入int?x=零;然后我试着读它:x.Value,然后我得到的是异常,而不是空的“Value”。如果我检查“x”本身是否为空,那么答案是肯定的。@Eru我在答案中使用了不正确的表达式,谢谢你指出这一点。关于不可见的
ref S
C
的第三和第四个要点有点不清楚。你能在它们上面展开吗?@Nisarg:当你有
class C{void M(int x){…}
并且你有
cc=new C();c、 M(123)然后,不知何故,
c
M
中变成了
this
。发生的方式是,调用在逻辑上与使用
类C{static void M(C_this,int x){…}
和调用
C.M(C,123)
时相同
在逻辑上只是另一个参数。类似地,对于structs,除了structs
之外,this
是变量的
ref
别名。
GetHashCode()
的实现只有在确定可以调用
GetHashCode()
时才起作用。
public override int GetHashCode()
{
    if (!this.HasValue)
    {
        return 0;
    }
    return this.value.GetHashCode();
}