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的正确答案:
是一种值类型。值类型由一个bool和一个T组成,bool表示空值(false表示空值),T表示值Nullable
- 与所有其他值类型不同,可空类型不会装箱到已装箱的
。它们将框设置为已装箱的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();
}