为什么 Nullable<T> 的 HasValue 属性在空值时不会抛出 NullReferenceException 异常?

24

考虑以下代码:

DateTime? ndate = null;
Console.WriteLine(ndate.HasValue);
我本来以为会抛出 NullReferenceException 异常,但实际上 HasValue 确实返回了 false。然而,既然 ndate 是 null,那么在没有对象的情况下如何成功调用该属性呢?
5个回答

47

从技术上讲,“ndate”不是null - 它是一个值类型,其值被指定为null。

当您编写DateTime?时,这只是Nullable<DateTime>的速记方式,它是一个结构体。由于它不是引用类型,因此从技术上讲无法为其设置null。


2
我假设 (ndate == null) 通过一个重载的运算符来运作? - Nathan
1
@Nathan:是的。编译器只是将其初始化为新的 Nullable<T>,默认情况下标记为 null。 - Reed Copsey

7
Nullable<T> 是一个结构体,基本上它不能保存 null 值。
实际上,您的任务编译后看起来像这样:
Nullable<DateTime> ndate = new Nullable<DateTime>();

另一个例子,表达式为:
int? a = null;

将生成以下IL代码:

.locals init (valuetype [mscorlib]System.Nullable`1<int32> V_0)
IL_0000:  ldloca.s   V_0
IL_0002:  initobj    valuetype [mscorlib]System.Nullable`1<int32>

一个调用initobj操作的过程,它将指定地址上的值类型的每个字段初始化为null引用或适当基元类型的0。
总之,这里发生的是默认的结构初始化

7
我本来期望会出现 NullReferenceException 异常。
但是,这里没有对 null 的引用,所以不应该期望出现 "NullReferenceException"。可空的 DateTime 不是一个 null 引用,而是一个 null 值。
然而,既然 ndate 是 null,那么如何可以成功调用属性 HasValue 呢?因为没有对象可以调用该属性啊。
这个问题基于错误的前提假设。其实是有一个对象存在的。这就是可空的 DateTime,表示可空的 DateTime 的 null 值。该值是一个对象。
请考虑下面的代码:
DateTime? ndate = null; 
Console.WriteLine(ndate.HasValue); 

这只是一种逻辑上类似于以下内容的简写方式:

DateTime ndate = default(DateTime); 
bool ndateHasValue = false;
Console.WriteLine(ndateHasValue); 

可空值类型是一种简单的编码方式,意味着“请将一个布尔值附加到此对象上,以跟踪它是否逻辑上为null”。


2
实际发生的情况是,一个非空的 Nullable 结构被分配给变量,因此变量不为 null,但其值为 null。

嗯...实际上,即使它的不为null,因为Nullable<T>有一个where T:struct约束,对吧?我会认为这只是内部的一个标志。 - Dan Tao

-4

语法糖,我的朋友……编译器将那个.HasValue调用转换为不会抛出异常的代码 :-)


并不完全正确 - ndate变量在技术上从未是“null”,因为它是一个值类型,不能为null。它的值为null,这是非常不同的。 - Reed Copsey
实际上,编译器将 ndate = null 转换为 ndate = new Nullable<DateTime>() - Greg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接