``??`` 运算符是不是使用短路?

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 ?? b 的类型取决于操作数类型之间可用的隐式转换。按优先顺序,a 的类型?? b 是 A0、A 或 B,其中 A 是 a 的类型,B 是 b 的类型(前提是 b 具有类型),如果 A 是可空类型,则 A0 是 A 的基础类型,否则 A .具体来说,a ?? b 被处理为 如下:

如果 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不兼容,会出现编译时错误。

第二个、第三个和第四个项目符号是相关的。


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 所迷惑——它们可能会造成严重的误导。”

以上是关于``??`` 运算符是不是使用短路?的主要内容,如果未能解决你的问题,请参考以下文章

&= 和 |= 运算符是不是用于布尔短路?

关于 Java 8(OCA)中的短路运算符和后缀增量的问题 [关闭]

lua中的逻辑运算符

Python是不是支持短路?

javascript短路运算

短路逻辑评估运算符