【发布时间】:2011-03-07 10:09:09
【问题描述】:
C# 中的?? 运算符在求值时是否使用短路?
var result = myObject ?? ExpressionWithSideEffects();
当myObject不为空时,ExpressionWithSideEffects()的结果不会被使用,但ExpressionWithSideEffects()会被完全跳过吗?
【问题讨论】:
标签: c# logic operator-keyword
C# 中的?? 运算符在求值时是否使用短路?
var result = myObject ?? ExpressionWithSideEffects();
当myObject不为空时,ExpressionWithSideEffects()的结果不会被使用,但ExpressionWithSideEffects()会被完全跳过吗?
【问题讨论】:
标签: c# logic operator-keyword
是的,确实短路了。
这是一个在 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();
第一个合并没有抛出异常,而第二个确实抛出异常(格式字符串无效)。
【讨论】:
是的。与以往一样,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 关于你碰巧使用的编译器是否是实际真相的来源......关于语言的真相是什么?它打算做什么或它目前做什么?
【讨论】:
这就是我们进行单元测试的原因。
[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");
}
这些不是最好的测试名称,但你明白了。它表明空值合并运算符按预期短路。
【讨论】: