这是由编译器控制的。所有带有[Conditional] 的方法仍将包含在MSIL 中,但将包含一个详细说明[Conditional] 的.custom instance 行。在为方法调用者编译时,编译器进行词法分析,然后进行语义分析和重载解析,并在您放置[Conditional] 的方法中找到.custom instance IL。因此它不会编译调用。
所以:编译器编译目标方法,但不编译对该方法的任何调用。注意:该方法仍然存在,您仍然可以通过反射调用它。见the spec
根据是否在调用点定义此符号来包含或省略对条件方法的调用。如果定义了符号,则包含调用;否则,调用(包括接收方的评估和调用的参数)将被省略。
如何验证?启动开发人员命令提示符,键入 ildasm <enter> 并打开相关的 dll/exe。查看调用者和被调用的[Conditional] 方法。您将看到被调用的方法具有带有.custom instance 的额外IL,并且在您期望的地方省略了调用者行。使用下面的代码在控制台应用程序上尝试一下。
为什么?在某些情况下,它使条件调用比使用#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();
}