使用可空类型的合并空运算符会改变隐式类型。

15

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

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?变为DateTime
实际上,在方法的剩余部分中,假设我使用的是
if (someCondition) dateTimeNullable2 = null;
那段代码是可以通过编译的,直到我让ReSharper用空值合并版本替换第二个表达式为止。
据我所知,用空值合并运算符替换原始代码会使ReSharper引发警告。
somevar != null ? somevar : somedefault;

使用

somevar ?? somedefault;

的确应该产生相同的结果。但对于可空类型的隐式类型转换,编译器似乎会将??视为它的含义。

somevar != null ? somevar.Value : somedefault;
所以我想问的是,为什么使用??会改变隐式类型,并且在文档中我可以找到关于此的信息在哪里。顺便说一句,这不是一个真实的场景,但我想知道为什么使用??会改变(隐式)类型。

4
为什么您期望 nullableDateTime ?? DateTime.Now 会产生一个 DateTime?,当编译器已经有足够的信息知道结果永远不会是 null - Damien_The_Unbeliever
@Damien:在我的第一个和第二个示例中,编译器也具有足够的信息来知道结果永远不会是“null”。这正是为什么我觉得这种行为有点奇怪的原因。 - comecme
1
但是 ?: 支持更广泛的可能输入(condition 不需要与任何一个结果 expression 相连)。因此,编译器架构针对 ?: 执行此分析是不寻常的。而对于 ??,它确切地知道结果将是第一个表达式,没有可能该表达式为空,或者是第二个表达式。 - Damien_The_Unbeliever
2个回答

9
你的前两个例子会引导你偏离正题;更好的方法是不考虑你自己,而是考虑那些可能会使用你的代码的人。
var dateTimeNullable1 = nullableDateTime.HasValue 
    ? nullableDateTime 
    : DateTime.Now;

但是相反,
var dateTimeNullable1 = nullableDateTime.HasValue 
    ? nullableDateTime.Value 
    : DateTime.Now;

引用C# 3.0规范中的第7.12节“空值合并运算符”(对于略微有些粗糙的格式表示歉意):
表达式“a ?? b”的类型取决于操作数类型之间可用的隐式转换。按优先级排序,“a ?? b”的类型为“A0”,“A”或“B”,其中“A”是“a”的类型,“B”是“b”的类型(只要“b”有一个类型),而“A0”是“A”的基础类型,如果“A”是可空类型,否则为“A”。因此,如果“a”是“Nullable ”,并且“b”可以隐式转换为“Something”,则整个表达式的类型将为“Something”。正如@Damien_The_Unbeliever所建议的那样,这个运算符的是为了合并空值!

1
到目前为止,我只在MSDN文档中搜索了??使用可空类型。它们在这些示例中从未使用过var,而是分配给一个明确的类型。无论如何,显然,当涉及可空类型时,a ?? ba!= null?a:b并不完全相同。 - comecme

5
从 C# 规范(第4版)来看,a ?? b 表达式的类型取决于操作数上有哪些隐式转换。按优先顺序,a ?? b 的类型为 A0AB,其中 Aa 的类型(如果 a 有类型),Bb 的类型(如果 b 有类型),而 A0A 的基础类型(如果 A 是可空类型),否则就是 A 本身。
因此,?? 明确规定了如果第一个表达式的底层类型是可空类型,则更喜欢第一个表达式的底层类型。
而对于处理 ?:7.14中的语言,只讨论了从形式 b ? x : y 中实际的 xy 的类型,以及这两种类型之间的隐式转换。
如果从 X 到 Y 存在隐式转换(§6.1),但从 Y 到 X 不存在,则 Y 是条件表达式的类型。
由于 Nullable(T) 定义了从 TNullable(T)隐式转换,并且只有从 Nullable(T)T 的显式转换,因此整个表达式的唯一可能类型是 Nullable(T)

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