考虑以下类别:
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会发现整个行没有被编译。
根据针对C#的最新ECMA标准(ECMA-334,即C#5.0),Conditional
属性置于方法上如下(强调我的意思):
如果在调用点定义了一个或多个与其相关的条件编译符号,则包括对条件方法的调用,否则将省略该调用。(第22.5.3节)
这似乎并不表示应忽略整个链条,因此是我的问题。话虽如此,Microsoft的C#6.0规范草案提供了更多细节:
如果定义了该符号,则包括该呼叫;否则,将忽略该呼叫(包括对接收方的评估和该呼叫的参数)。
没有对调用的参数进行评估的事实有据可查,因为这是人们#if
在功能体内使用此功能而不是使用伪指令的原因之一。但是,有关“接收者评估”的部分是新的-我似乎无法在其他地方找到它,并且确实可以解释上述行为。
有鉴于此,我的问题是:在这种情况下,C#编译器不进行评估的原理 是什么a.GetB()
?根据条件调用的接收者是否存储在临时变量中,它的行为是否真的有所不同?
B
在方法链版本中未保留对它的引用,因此编译器会忽略它的创建,因为它错误地“认为”您只想调用该Hello()
方法。好问题!