我有一些标记为[Conditional("XXX")]
的助手方法。其目的是使方法在只存在xxx条件编译符号时进行有条件编译。我们使用它来调试和跟踪功能,它工作得很好。
在我研究条件编译工作原理的过程中,我发现有几个声明带有Conditional
属性的方法的源将被放在il中,但是对这些方法的调用将不会被执行。
如何将代码编译成IL但不执行?如何验证行为是否如所述?我没有做太多的IL,所以我的技能在这方面有点薄弱。
最佳答案
这是由编译器控制的。带有[Conditional]
的所有方法仍将包含在msil中,但将包含一行.custom instance
来详细说明[Conditional]
。在方法调用程序的编译时,编译器会分析语法,然后进行语义分析和重载解析,并在放置.custom instance
的方法中找到[Conditional]
il。因此它不会编译调用。
所以:编译器编译目标方法,但不编译对该方法的任何调用。注意:方法仍然存在,您仍然可以通过反射调用它。见the spec
对条件方法的调用是包含还是省略取决于此符号是否在调用点定义。如果定义了符号,则包含调用;否则,将忽略调用(包括对调用参数的求值)。
你怎么能证实?启动developer命令提示符,键入ildasm <enter>
并打开相关的dlls/exes。查看调用方和被调用的[Conditional]
方法。您将看到被调用的方法有额外的il和.custom instance
,调用行在您期望的地方被省略。在控制台应用程序上使用下面的代码进行尝试。
为什么?在某些情况下,它使条件调用比使用#if
更简单。见Eric Lippert: What's the difference between conditional compilation and the conditional attribute?
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?");
}
}
在相应的msil中,包含了
VerboseEmit
,但不从Main
调用:.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
加分。查看控制台输出和msil(相应地修改emit方法):
static void Main(string[] args)
{
int callCount = 0;
AlwaysEmit(++callCount);
VerboseEmit(++callCount);
DebugEmit(++callCount);
Console.WriteLine("Call count = " + callCount);
Console.ReadLine();
}