“?”运算符除了检查 null 还做其他事情吗?

Does the "?." operator do anything else apart from checking for null?

如您所知,DateTime? 没有参数化的 ToString(用于格式化输出),并执行类似

的操作
DateTime? dt = DateTime.Now;
string x;
if(dt != null)
    x = dt.ToString("dd/MM/yyyy");

会抛出

No overload for method 'ToString' takes 1 arguments

但是,自从 C# 6.0 和 Elvis (?.) 运算符,上面的代码可以替换为

x = dt?.ToString("dd/MM/yyyy");

哪个....有效!为什么?

因为 Nullable<T> 在 C# 中的实现方式使该结构的实例显示为可空类型。当你有 DateTime? 时,它实际上是 Nullable<DateTime>,当你将 null 分配给它时,你在幕后将 HasValue 设置为 false,当你检查null,您正在检查 HasValue,等等。?. 运算符只是以一种方式实现的,它取代了适用于引用类型和可空结构的相同习语。就像语言的其余部分使可空结构类似于引用类型一样(关于 null-ness)。

简答:

DateTime? 只是 Nullable<DateTime> 的甜美语法,它不包含 DateTime 的属性和方法,而 是 Elvis 运算符适用于不可空 Nullable<DateTime>.Value.


解释:

以下代码:

DateTime? dt = DateTime.Now;
string x;
if (dt != null)
    x = dt?.ToString("dd/MM/yyyy");

当反编译为 C# 5.0 时会产生以下结果:

DateTime? nullable = new DateTime?(DateTime.Now);
if (nullable.HasValue)
{
    string str = nullable.HasValue ? nullable.GetValueOrDefault().ToString("dd/MM/yyyy") : null;
}

旁注:string 似乎在 if 中声明是不相关的,因为在 MSIL 级别提升,并且由于稍后在反编译器上不使用该值,就好像它是在 if 范围内声明的一样。

如您所见,由于 DateTime? 只是 Nullable<DateTime> 的一个甜美语法,C#Nullable<T> 有一个特定的参考 Elvis 运算符,使其return 值 不可为空的T 本身.

整个Elvis operator的结果必须是Nullable 因此,如果你想收到一个非 string 值它必须是 Nullable<T>ReferenceType 但这不会改变这样一个事实,即如果操作员已经设法获得 Nullable<DateTime> 值本身- returned DateTime 不再是 Nullable<DateTime>

正在考虑:

DateTime? dt = DateTime.Now;
string x;
if(dt != null)
    x = dt.ToString("dd/MM/yyyy");

此处 dtDateTime?Nullable<DateTime> 不是 IFormatable 并且没有 ToString(string format) 方法。

所以它抛出。

现在考虑:

x = dt?.ToString("dd/MM/yyyy");

?. 是语法糖:

dt.HasValue ? dt.Value.ToString("dd/MM/yyyy"): null

这里 dt.Value 是一个 DateTimeIFormatable 并且有一个 ToString(string format) 方法。

最后,用 C# 5.0 编写第一个代码的好方法是:

DateTime? dt = DateTime.Now;
string x;
if(dt.HasValue)
    x = dt.Value.ToString("dd/MM/yyyy");