首页 > 解决方案 > 参数是否有合并运算符?

问题描述

如果F是“开” a,我可以这样做......

var obj = a?.F();

如果F没有打开a,我必须这样做......

var obj = a == null ? null : MyFunc.F((A) a);

还是我?如果参数值为空,是否有更简洁的方法来跳过方法调用?

标签: c#

解决方案


简短的回答是否定的,没有简洁的方法可以做到这一点。

稍长一点的答案仍然是否定的,但这里有一个有趣的语言设计点。C# 是由品味极高的人设计的,如果我自己这么说的话,但它是在很长一段时间内设计的。这一点在其对可空性的处理方面最为明显。

在 C# 1.0 中,我们有一种传统的 C、C++、Java、JavaScript 等语言,其中有引用和值,并且引用可以为空。这有好处;如果不是这样,Tony 爵士一开始就不会发明空引用。但它也有缺点:我们有可能出现空引用,取消引用 null 会导致程序崩溃,并且引用类型和值类型之间存在不一致:引用类型具有天然的“无值”值,而值类型则没有。

在 C# 2.0 中,我们添加了可空值类型,但可空值类型的行为与可空引用类型不同。当然,可空值类型不是引用,所以你不能“取消引用”它们,但是如果我们稍微眯一下,这个.Value属性看起来很像“取消引用”,如果值为 null,它会导致类似的崩溃。从这个意义上说,它们的行为是相同的,但在其他意义上,它们却不同。如果其中一个为空,则将两个可为空的整数相加不会崩溃;相反,结果也是空的。

所以在这一点上,我们在语言中内置了一个矛盾:

使用可空值类型的空值通常会自动传播空值,但使用空引用可能会崩溃。

当然,随后 C# 继续添加了各种特性,使空引用的行为更像空值,?.以及相关的操作。还有一些非常令人兴奋的 C# 8 提案,它们将支持“不可为空的引用类型”方案。

但是上面的粗体文本是您已经指出的基本问题:可空引用类型上的运算符的语义几乎总是“将运算符的不可空版本提升为可空类型;如果所有操作数都是非空的,那么结果与未提升版本相同;否则,结果为空"。但是,这些语义不会自动扩展到.成员访问运算符或()调用运算符,无论操作数是可空值类型还是可空引用类型。.可以显式提升,?.()运算符永远不会提升为可为空的语义。

想象一下像 C# 1.0 这样的语言,但从Nullable<T>一开始就是内置的,因此它适用于引用类型和值类型。在那个世界里,你可以看到实现广义提升的自然方法,如果你有一个方法

class C { string M(double, int[]) }

并且您使用Nullable<C>接收器或Nullable<double>参数调用它Nullable<int[]>,您会自动获得我们为可空整数算术构建的代码:检查接收器或任何参数是否为空,如果是,则返回 null Nullable<string>。否则,正常调用该函数并使用不可为空的结果。

C# 编译器已经为结构类型上声明的所有用户定义的运算符实现了这些语义;将这些语义扩展到其他类型的方法几乎没有任何困难。但我们现在做不到;有太多的向后兼容性问题需要解决。

这种设计选择还具有很好的特性,即它是“可能是单子”的正确实现。

但这不是我们所处的世界,这在很大程度上是因为这些设计考虑是随着时间的推移而演变的,而不是一次性发明出来的。下次您发明一种新语言时,请仔细考虑如何表示可空性!


推荐阅读