在可空类型上使用合并空运算符会更改隐式类型

Posted

技术标签:

【中文标题】在可空类型上使用合并空运算符会更改隐式类型【英文标题】:using coalescing null operator on nullable types changes implicit type 【发布时间】:2012-04-27 13:47:07 【问题描述】:

我希望接下来的三行代码是相同的:

public static void TestVarCoalescing(DateTime? nullableDateTime)

  var dateTimeNullable1 = nullableDateTime.HasValue ? nullableDateTime : DateTime.Now;
  var dateTimeNullable2 = nullableDateTime != null ? nullableDateTime : DateTime.Now;
  var dateTimeWhatType = nullableDateTime ?? DateTime.Now;

在所有情况下,我都将nullableDateTime 分配给新变量。我希望所有变量的类型都变为DateTime?,因为那是nullableDateTime 的类型。但令我惊讶的是,dateTimeWhatType 的类型只是变成了DateTime,所以不能为空。

更糟糕的是,ReSharper 建议将第二个语句替换为空合并表达式,将其转换为表达式 3。因此,如果我让 ReSharper 做它的事情,变量的类型将从 DateTime? 更改为 @987654328 @。

事实上,假设在方法的其余部分,我会使用

if (someCondition) dateTimeNullable2 = null;

这会编译得很好,直到我让 ReSharper 用空合并版本替换第二个表达式。

AFAIK,替换

somevar != null ? somevar : somedefault;

somevar ?? somedefault;

确实应该产生相同的结果。但是对于可空类型的隐式类型,编译器似乎威胁??,就好像它的意思一样。

somevar != null ? somevar.Value : somedefault;

所以我想我的问题是为什么当我使用 ?? 时隐式类型会发生变化,以及在文档中我可以找到有关此信息的位置。

顺便说一句,这不是真实世界的场景,但我想知道为什么使用 ?? 会改变(隐式)类型。

【问题讨论】:

当编译器有足够的信息知道结果永远不会是null时,你为什么期望nullableDateTime ?? DateTime.Now产生DateTime? @Damien:在我的第一个和第二个示例中,编译器也有足够的信息知道结果永远不会是null。这正是我觉得这种行为有点奇怪的原因。 但是?: 支持更大范围的可能输入(根本没有理由将condition 连接到任一结果expression)。因此,编译器设计为针对?: 执行此分析是不寻常的。而对于??,它确切地知道结果将是第一个表达式,该表达式不可能为空,或者是第二个表达式。 【参考方案1】:

你的前两个例子让你误入歧途;最好不要考虑你的

var dateTimeNullable1 = nullableDateTime.HasValue 
    ? nullableDateTime 
    : DateTime.Now;

而是

var dateTimeNullable1 = nullableDateTime.HasValue 
    ? nullableDateTime.Value 
    : DateTime.Now;

引用 C# 3.0 规范的第 7.12 节“空合并运算符”(对于稍微粗暴的格式表示歉意):

表达式a ?? b 的类型取决于哪个隐式 可以在操作数类型之间进行转换。为了 首选,a ?? b 的类型是 A0AB, 其中Aa 的类型,Bb 的类型(前提是 b 有一个类型),A0A 的底层类型 if A 是可空类型,否则 A

所以如果aNullable<Something>,并且b可以隐式转换为Something,那么整个表达式的类型就是Something。正如@Damien_The_Unbeliever 建议的那样,这个运算符的 point 是合并空值!

【讨论】:

到目前为止,我只搜索了MSDN documentation on ?? 和Using Nullable Types。他们在这些示例中从不使用var,而是分配给显式类型。无论如何,在可空类型方面,a ?? b 显然a != null ? a : b 完全相同。【参考方案2】:

请走一下所有语言的律师。来自 C# 规范(版本 4):

7.13

表达式a ?? b 的类型取决于操作数上可用的隐式转换。按优先顺序,a ?? b 的类型为A0AB,其中Aa 的类型(前提是a 有类型),Bb 的类型(前提是b 有一个类型),如果A 是可空类型,A0A 的基础类型,否则A

因此,?? 被明确定义为首选第一个表达式的基础类型,如果第一个表达式是可为空的类型。

7.14中的语言(处理?:)只讨论xy的实际类型,从b ? x : y的形式,并讨论隐式 这两种类型之间的转换。

如果存在从 X 到 Y,但不存在从 Y 到 X 的隐式转换(第 6.1 节),则 Y 是条件表达式的类型

由于Nullable(T) 定义了从TNullable(T)隐式 转换,并且只定义了从Nullable(T)T显式 转换,因此唯一的整体表达式的可能类型是Nullable(T)

【讨论】:

以上是关于在可空类型上使用合并空运算符会更改隐式类型的主要内容,如果未能解决你的问题,请参考以下文章

如何将可空类型隐式转换为不可空类型

可空类型和赋值运算符

可空类型和三元运算符:为啥是`? 10:null`禁止? [复制]

#5 kotlin nullable 可空类型

可空类型 int?及?相关运算符

? 的可空类型问题:条件运算符