如果F
位于a
上,我可以这样做...
var obj = a?.F();
如果F
不在a
上,我必须这样做...
var obj = a == null ? null : MyFunc.F((A) a);
还是我?如果参数值为null,是否有更简洁的方法跳过方法调用?
答案 0 :(得分:8)
简短的回答是不,没有简洁的方法。
稍长一点的答案仍然是否,但是这里有一个有趣的语言设计要点。如果我自己说的话,C#是由具有极佳品味的人设计的,但是它是经过很长时间设计的。在处理可空性方面,没有什么比这更明显了。
在C#1.0中,我们沿袭了C,C ++,Java,JavaScript等传统语言,其中包含引用和值,并且引用可以为null。这有好处;如果没有的话,托尼爵士根本就不会发明空引用。但这有缺点:我们可能会使用null引用,将null解引用会导致程序崩溃,并且引用和值类型之间存在不一致的地方:引用类型具有自然的“无值”值,而值类型则没有。 >
在C#2.0中,我们添加了可为空的值类型,但是可为空的值类型的行为不同于可为空的引用类型。当然,可为空的值类型不是引用,因此您不能“解引用”它们,但是如果斜视一下,.Value
属性看起来很像“解引用”,如果值是,则会导致类似的崩溃。空值。从某种意义上说,它们的行为相同,但从其他意义上讲,它们却不一样。如果将两个可为空的整数加在一起,则其中之一为null不会崩溃。而是结果也为空。
因此,在这一点上,我们的语言中存在一个矛盾:
使用可为null的值类型的null值通常会自动传播null,但使用null引用可能会崩溃。
当然,C#随后继续添加各种功能,这些功能使空引用的行为更像空值,例如?.
和相关操作。对于C#8也有一些非常令人兴奋的建议,它们将支持“不可为空的引用类型”方案。
但是上面加粗的文本是您已经指出的基本问题:可空引用类型上的运算符的语义几乎总是“将运算符的非空版本升级为可空类型;如果所有操作数都不为空那么结果与未提升版本相同;否则,结果为null”。但是,无论操作数是可为空的值类型还是可为空的引用类型,这些语义都不会不会自动扩展到.
成员访问运算符或()
调用运算符。 .
可以明确地提升?.
,但是()
运算符永远不会提升为可为空的语义。
想象一下像C#1.0这样的语言,但是从一开始就内置了Nullable<T>
,因此它既适用于引用类型,也适用于值类型。在那个世界上,您可以看到实现通用提升的自然方法,如果有的话
class C { string M(double, int[]) }
,然后使用Nullable<C>
接收器或Nullable<double>
和Nullable<int[]>
参数调用它,您将自动获得我们为您创建的用于可空整数算法的代码:检查接收器还是接收器任何参数都为null,如果为null,则结果为Nullable<string>
。否则,请正常调用该函数并使用不可为空的结果。
C#编译器已经已经为在结构类型上声明的所有用户定义的运算符实现了这些语义;将这些语义扩展到其他类型的方法几乎没有任何困难。但是我们现在不能做到;有太多向后兼容的问题要解决。
此设计选择还具有不错的属性,它可以正确地实现“也许是单子”。
但这不是我们所处的世界,很大程度上是因为这些设计考虑因素是随着时间的推移而演变的,而不是一次被发明出来的。下次您发明一种新语言时,请仔细考虑如何表示可空性!