考虑以下类:
public class A {
public B GetB() {
Console.WriteLine("GetB");
return new B();
}
}
public class B {
[System.Diagnostics.Conditional("DEBUG")]
public void Hello() {
Console.WriteLine("Hello");
}
}
现在,如果我们这样调用方法:
var a = new A();
var b = a.GetB();
b.Hello();
在发布版本中(即没有 DEBUG
标志),我们只会看到 GetB
打印在控制台上,作为对 Hello()
的调用会被编译器省略。在调试版本中,两个打印都会出现。
现在让我们链接方法调用:
a.GetB().Hello();
调试构建中的行为没有改变;但是,如果未设置该标志,我们会得到不同的结果:两个 调用都被省略,控制台上也没有打印。快速查看 IL 显示整行未编译。
根据latest ECMA standard for C# (ECMA-334,即 C# 5.0),Conditional
时的预期行为方法上的属性如下(强调我的):
A call to a conditional method is included if one or more of its associated conditional compilation symbols is defined at the point of call, otherwise the call is omitted. (§22.5.3)
这似乎并不表示应该忽略整个链条,因此我的问题。也就是说,C# 6.0 draft spec from Microsoft提供更多细节:
If the symbol is defined, the call is included; otherwise, the call (including evaluation of the receiver and parameters of the call) is omitted.
调用参数未被评估的事实有据可查,因为这是人们使用此功能而不是 #if
的原因之一。函数体中的指令。然而,关于“接收者的评估”的部分是新的——我似乎无法在其他地方找到它,它似乎确实解释了上述行为。
鉴于此,我的问题是:C# 编译器不评估的基本原理是什么 a.GetB()
在这种情况下?它真的应该根据条件调用的接收者是否存储在临时变量中而表现不同吗?
最佳答案
归结为短语:
(including evaluation of the receiver and parameters of the call) is omitted.
在表达式中:
a.GetB().Hello();
“接收者的评价”是:a.GetB()
。所以:根据规范,它被省略了,这是一个有用的技巧,允许[Conditional]
避免未使用的东西的开销.当你把它放到本地时:
var b = a.GetB();
b.Hello();
那么“接受者的求值”只是局部的b
,但是原始的var b = a.GetB();
仍然被求值(即使本地 b
最终被删除)。
这可能会产生意想不到的后果,因此:请谨慎使用[Conditional]
。但原因是可以轻松添加和删除诸如日志记录和调试之类的东西。请注意,如果处理不当,参数也可能有问题:
LogStatus("added: " + engine.DoImportantStuff());
和:
var count = engine.DoImportantStuff();
LogStatus("added: " + count);
如果 LogStatus
被标记为 [Conditional]
,可能非常不同 - 结果是您的实际“重要内容”没有得到完成。
关于c# - 当最后一个方法调用是有条件的时,为什么 C# 编译器会删除一连串的方法调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49253356/