``??`` 运算符是不是使用短路?
Posted
技术标签:
【中文标题】``??`` 运算符是不是使用短路?【英文标题】:Does the ``??`` operator use shortcircuiting?``??`` 运算符是否使用短路? 【发布时间】:2011-03-07 10:09:09 【问题描述】:C# 中的??
运算符在求值时是否使用短路?
var result = myObject ?? ExpressionWithSideEffects();
当myObject
不为空时,ExpressionWithSideEffects()
的结果不会被使用,但ExpressionWithSideEffects()
会被完全跳过吗?
【问题讨论】:
【参考方案1】:是的,确实短路了。
这是一个在 LinqPad 中测试的 sn-p:
string bar = "lol";
string foo = bar ?? string.Format("2", 1);
foo.Dump();
bar = null;
foo = bar ?? string.Format("2", 1);
foo.Dump();
第一个合并没有抛出异常,而第二个确实抛出异常(格式字符串无效)。
【讨论】:
废话,我能感觉到自己被拉进了事件视界!【参考方案2】:是的。与以往一样,C# 语言规范是权威来源1。
来自 C# 3 规范,第 7.12 节(v3 而不是 4,因为 v4 规范涉及在这里并不真正相关的动态细节):
表达式
如果 A 不是可空类型或引用类型,则编译时错误 发生。 如果 A 是可空类型并且存在从 b 到的隐式转换 A0,结果类型为A0。在 运行时,首先评估 a。如果一个 不为空,a 被解包以输入 A0,这就是结果。 否则,评估 b 并且 转换为 A0 类型,这变为 结果。 否则,如果存在从 b 到 A 的隐式转换,则结果类型为 A. 在运行时,首先评估 a。 如果 a 不为空,则 a 变为 结果。否则,评估 b 并且 转换为 A 型,这就变成了 结果。 否则,如果 b 具有 B 类型并且存在从 A0 到 B,结果类型为 B。在运行时, a首先被评估。如果不是 null,a 被解包为 A0 类型 (除非 A 和 A0 是同一类型) 并转换为 B 型,这 变成结果。否则,b 是 评估并成为结果。 否则a和b不兼容,会出现编译时错误。a ?? b
的类型取决于操作数类型之间可用的隐式转换。按优先顺序,a 的类型?? b 是 A0、A 或 B,其中 A 是 a 的类型,B 是 b 的类型(前提是 b 具有类型),如果 A 是可空类型,则 A0 是 A 的基础类型,否则 A .具体来说,a ?? b
被处理为 如下:
第二个、第三个和第四个项目符号是相关的。
1 关于你碰巧使用的编译器是否是实际真相的来源......关于语言的真相是什么?它打算做什么或它目前做什么?
【讨论】:
脚注...我想这就是为什么我们都喜欢 Eric Lippert 的存在:) @Matthew:众多原因之一,是的。 Eric 的一个有趣的方面是他可以充当规范 和 编译器的人类化身......【参考方案3】:这就是我们进行单元测试的原因。
[TestMethod]
public void ShortCircuitNullCoalesceTest()
const string foo = "foo";
var result = foo ?? Bar();
Assert.AreEqual(result, foo);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShortCircuitNullCoalesceFails()
const string foo = null;
var result = foo ?? Bar();
private static string Bar()
throw new ArgumentException("Bar was called");
这些不是最好的测试名称,但你明白了。它表明空值合并运算符按预期短路。
【讨论】:
我意识到 ArgumentException 是一个奇怪的选择,它只是第一个想到的异常类型。 这不是我们进行单元测试的原因。这就是我们有语言规范的原因。特别是,如果我们有单元测试但没有语言规范,我们只会知道它在被测试的情况下会发生什么。但是,如果我们有语言规范但没有单元测试,我们仍然会知道该语言在一般情况下的用途。诚然,单元测试有助于验证编译器是否确实实现了语言规范......但我总是更愿意接触规范而不是单元测试来解决这样的问题。 @Jon Skeet,触摸。我仍然喜欢编写快速测试来验证我不确定的事情。我不一定会保留它。而且编译器总是有可能不正确地实现规范...... 编译器也有可能不正确地实现了规范——只是不适合您碰巧选择的情况。事实上,我怀疑简单案例比晦涩的案例更有可能正确:) @Jon Skeet,当然,我同意,这将是一个很难搞砸的问题......而且我不会在编写它以了解编译器如何工作后保留这个测试(这比阅读文档要快)。但要重复一句最喜欢的名言:“不要被 cmets 所迷惑——它们可能会造成严重的误导。”以上是关于``??`` 运算符是不是使用短路?的主要内容,如果未能解决你的问题,请参考以下文章