空值合并运算符是如何工作的

6
我有三个类(classA、classB和classC),它们都继承自一个接口“IFoo”。如果使用以下代码:
var fooItem = (request.classAitem ?? (request.classBitem  as IFoo ?? request.classCitem)) 

或者
var fooItem = (request.classAitem ?? request.classBitem ?? request.classCitem as IFoo)

它可以正常工作,但其他组合甚至无法编译:

var fooItem = (request.classAitem as IFoo ?? request.classBitem ?? request.classCitem)

或者

var fooItem = (request.classAitem ?? request.classBitem ?? request.classCitem) as IFoo

在某些情况下,编译器似乎会将子类隐式地解压缩为它们的IFoo接口,但在其他一些情况下则不会。你们认为呢?


2
classAitemclassBitemclassCitem有哪些类型?请展示它们的定义。另外,ContactPoint是什么?它实现了IFoo吗? - poke
无法编译,但是出现了什么错误? - Doruk
请发布一个 [mcve]。 - Lasse V. Karlsen
@poke,我重新设计了代码示例classAitem、classBItem和classCitem,它们的类型分别为classA、classB和classC,并且它们仅实现了IFoo接口。 - Zalomon
1个回答

5
在你所举的两个不起作用的例子中,因为??是右结合的,我们首先尝试确定这个表达式的数据类型:
request.classBitem ?? request.classCitem

数据类型只能是其输入数据类型之一,如果数据类型不同,则无法进行任何方向的转换,因此会出现编译器错误。请注意,编译器不会仅仅因为这两个类都实现了接口“IFoo”(如果它确实这样做了,那么如果它们都实现了多个公共接口,会发生什么?)而决定数据类型在这里是“IFoo”。
将其与您的前两个示例进行比较。在第一个示例中,我们首先考虑以下表达式:
request.classBitem  as IFoo ?? request.classCitem

这个表达式的类型是IFoo或者是request.classCitem的数据类型。有一个从request.classCitem的类型到IFoo的转换,因此明显选择后者,整个表达式的数据类型也就是IFoo。然后使用它来确定整个表达式的类型(也是IFoo)。
第二种情况非常相似,因为 ?? 是右结合的1,我们首先要确定的是表达式的类型:
request.classBitem ?? request.classCitem as IFoo

再次,我们需要在IFoorequest.classBitem的数据类型之间做出选择。由于有一种转换可以将request.classBitem转为IFoo,因此会选择IFoo

1注意,这也意味着第一个例子中的括号是多余的。


我理解你的观点:它从右侧开始评估完整表达式,“var fooItem = (request.classAitem as ContactPoint ?? request.classBitem ?? request.classCitem)”不起作用,因为它首先评估“request.classBitem ?? request.classCitem”,这是无法解决的,因为如你所说,classB和classC可以共享任意数量的接口。非常好的答案。 - Zalomon
1
@Zalomon - 我小心避免使用“评估”这个词 - 因为如果classAitem不是null,它将不会评估 classBitemclassCitem。但它确实需要确定整个表达式的数据类型,这需要考虑所有潜在输入的数据类型。 - Damien_The_Unbeliever

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