【发布时间】:2018-08-21 13:02:03
【问题描述】:
考虑以下类:
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 属性放在方法上时的预期行为如下(强调我的):
如果一个或多个关联的条件编译符号是,则包括对条件方法的调用 在调用点定义,否则调用被省略。 (§22.5.3)
这似乎并不表明应该忽略整个链条,因此我提出了问题。话虽如此,C# 6.0 draft spec from Microsoft 提供了更多细节:
如果定义了符号,则包含调用;否则,调用(包括对接收者的评估和调用的参数)将被省略。
调用的参数没有被评估的事实是有据可查的,因为这是人们使用此功能而不是函数体中的#if 指令的原因之一。然而,关于“接收者评估”的部分是新的 - 我似乎无法在其他地方找到它,它似乎确实解释了上述行为。
鉴于此,我的问题是:C# 编译器不评估的原因是什么 a.GetB() 在这种情况下?条件调用的接收者是否存储在临时变量中?
【问题讨论】:
-
这只是一个猜测,但我认为由于您没有在方法链接版本中保留对
B的引用,因此编译器会忽略它的创建,因为它错误地“认为”您只想调用Hello()方法。好问题! -
@ZoharPeled 谢谢!后来我注意到 C# 6.0 草案中添加的细节,但行为早于该草案。它似乎确实表明这是预期的行为,所以我现在最感兴趣的是这背后的基本原理:为什么这是预期的,为什么直到最近才记录下来。
-
如果您将
this视为附加的隐藏方法参数(确实如此),则更有意义。它只是得到与所有其他参数相同的处理。 -
@LucasTrzesniewski 这是一个合理的解释——它可能会比现在被赞成的答案更好。
-
我目前还没有打开 Visual Studio 来查看 - 但如果
a.GetB().Hello();行上有某种视觉指示表明它受此约束,那就太好了消失的行为。因为否则这种语义对读者来说是相当不可见的。
标签: c# conditional-compilation