??
C# 中的运算符在评估时是否使用短路?
var result = myObject ?? ExpressionWithSideEffects();
当myObject
为非空时,ExpressionWithSideEffects()
不使用的结果,而是ExpressionWithSideEffects()
完全跳过?
??
C# 中的运算符在评估时是否使用短路?
var result = myObject ?? ExpressionWithSideEffects();
当myObject
为非空时,ExpressionWithSideEffects()
不使用的结果,而是ExpressionWithSideEffects()
完全跳过?
是的,确实短路了。
这是在 LinqPad 中测试的片段:
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 不为 null,则将 a 解包为类型 A0,这将成为结果。否则,b 被求值并转换为 A0 类型,这成为结果。
- 否则,如果存在从 b 到 A 的隐式转换,则结果类型为 A。在运行时,首先评估 a。如果 a 不为空,则 a 成为结果。否则,b 被评估并转换为类型 A,这成为结果。
- 否则,如果 b 具有 B 类型并且存在从 A0 到 B 的隐式转换,则结果类型为 B。在运行时,首先计算 a。如果 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");
}
这些不是最好的测试名称,但你明白了。它表明空值合并运算符按预期短路。