考虑以下类别:
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()
?根据条件调用的接收者是否存储在临时变量中,它的行为是否真的有所不同?
我做了一些挖掘,发现C#5.0语言规范实际上确实已经在第424页上的17.4.2节的Conditional属性中包含您的第二个引号。
马克·格雷韦尔(Marc Gravell)的答案已经表明,这种行为是有意的,在实践中是什么意思。您还询问了理由背后却似乎被去除开销马克的被提及的不满。
也许您想知道为什么将其视为可以消除的开销?
a.GetB().Hello();
在您的场景中根本没有被调用而Hello()
被忽略,从表面上看可能很奇怪。
我不知道该决定背后的理由,但我发现了一些合理的推理依据。也许它也可以为您提供帮助。
仅当每个先前的方法都有返回值时,才可以进行方法链接。当您想对这些值做某事时,这很有意义,即a.GetFoos().MakeBars().AnnounceBars();
如果您有一个只执行某些操作而没有返回值的函数,则您不能在其后面链接某些东西,而可以将其放在方法链的末尾,就像您的条件方法一样,因为它必须具有返回类型void。
还要注意,前面的方法调用的结果被丢弃了,因此在您的示例中,执行此语句后a.GetB().Hello();
的结果GetB()
没有理由继续存在。基本上,您暗示您GetB()
只需要使用的结果Hello()
。
如果Hello()
省略了您为什么要GetB()
呢?如果您忽略Hello()
了行,则将其归结为a.GetB();
没有任何分配,并且许多工具会警告您您没有使用返回值,因为这很少是您想要执行的操作。
您似乎对此方法不满意的原因不仅是尝试执行返回某个值所需的操作,而且还产生副作用,即I / O。如果您确实有一个纯函数,那么实际上就没有理由GetB()
忽略后续调用,即,如果您不打算对结果做任何事情。
如果将的结果分配给GetB()
变量,则它本身就是一条语句,并且无论如何都会执行。所以这种推理解释了为什么
var b = a.GetB();
b.Hello();
Hello()
当使用方法链接时,仅省略对的调用,而整个链被忽略。
您也可以在完全不同的地方查看以获得更好的视角:C#6.0中引入的空条件运算符或elvis运算符 ?
。尽管对于带有null检查的更复杂的表达式来说,它只是语法糖,但它允许您构建类似于方法链之类的方法,并可以选择基于null检查的短路方式。
例如,GetFoos()?.MakeBars()?.AnnounceBars();
如果先前的方法不返回null
,则它将到达结尾,否则将省略后续的调用。
这可能是反直觉的,但试试你的情况想象成这个倒数:编译器之前忽略你的电话Hello()
在你的a.GetB().Hello();
链,因为你没有达到链的末端反正。
所有这些都是扶手椅式的推理,因此,请与猫王操作员进行一番比较,然后撒一粒盐。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句