我有一些标有[Conditional("XXX")]
的辅助方法。目的是在仅存在XXX条件编译符号时使方法有条件地编译。我们正在使用它来调试和跟踪功能,它运行良好。
在我研究条件编译的工作原理时,我发现有几个来源说明用Conditional
属性标记的方法将放在IL中,但不会执行对方法的调用。
代码如何编译成IL而不执行?如何验证行为实际上是如上所述?我没有做过很多IL,所以我的技能在这个领域都没什么弱点。
答案 0 :(得分:9)
这是由编译器控制的。 [Conditional]
的所有方法仍将包含在MSIL中,但会包含详细.custom instance
的{{1}}行。在编译器编译器编译时编译,然后进行语义分析和重载解析,并在放置[Conditional]
的方法中找到.custom instance
IL。因此它不会编译呼叫。
所以:编译器编译目标方法,但不编译该方法的任何调用。注意:该方法仍然存在,您仍然可以使用反射调用它。见the spec
根据是否在调用点定义了此符号,可以包含或省略对条件方法的调用。如果定义了符号,则包括呼叫;否则,省略呼叫(包括对呼叫参数的评估)。
您如何验证它?启动开发人员命令提示符,键入[Conditional]
并打开相关的dll / exes。查看调用者和被调用的ildasm <enter>
方法。您将看到被调用的方法具有[Conditional]
的额外IL,并且在您期望的位置省略了调用者行。使用下面的代码在控制台应用程序上尝试。
为什么呢?在某些情况下,它使条件调用比使用.custom instance
更简单。见Eric Lippert: What's the difference between conditional compilation and the conditional attribute?
#if
在相应的MSIL中,包含class Program
{
static void Main(string[] args)
{
AlwaysEmit();
DebugEmit();
VerboseEmit();
}
public static void AlwaysEmit()
{
Console.WriteLine("Beam me up");
}
[Conditional("DEBUG")]
public static void DebugEmit()
{
Console.WriteLine("Kirk out");
}
[Conditional("VERBOSE")]
public static void VerboseEmit()
{
Console.WriteLine("Say that again?");
}
}
,但未从VerboseEmit
调用:
Main
奖励积分。查看控制台输出和MSIL(相应地修改Emit方法):
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: call void RateScope.SdrApi.UploaderConsoleApp.Program::AlwaysEmit()
IL_0006: nop
IL_0007: call void RateScope.SdrApi.UploaderConsoleApp.Program::DebugEmit()
IL_000c: nop
IL_000d: ret
} // end of method Program::Main
...
.method public hidebysig static void VerboseEmit() cil managed
{
.custom instance void [mscorlib]System.Diagnostics.ConditionalAttribute::.ctor(string)
= ( 01 00 07 56 45 52 42 4F 53 45 00 00 ) // ...VERBOSE..
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Say that again\?"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Program::VerboseEmit
答案 1 :(得分:7)
(已经辩论过这是否有资格作为答案,但感觉值得一提。如果人们不同意DV或评论,我会很乐意删除)
这会影响调用站点而不是方法本身的一个重要特征是此功能在程序集中工作,并且它是影响调用站点范围的编译符号是否调用了调用。
因此,必须将实际方法发送到已编译的程序集中的一个原因是,在那时,实际上并不知道是否要调用该方法。
现在,在编译消费应用程序的时候,我们实际上知道该方法是否被使用。这也意味着在复杂的解决方案中,多个级别有多个消费者,某些调用可能会发生(在某些项目中)而其他人则不会。
答案 2 :(得分:1)