This is controlled by the compiler. All methods with [Conditional]
will still be included in the MSIL, but will include a .custom instance
line that details the [Conditional]
. At compile time for a method caller the compiler lexes, then does semantic analysis and overload resolution, and finds the .custom instance
IL in the method you placed [Conditional]
on. It therefore doesn't compile the call.
So: the compiler compiles the target method, but does not compile any call to that method. Note: the method is still there and you could still call it with reflection. See the spec
Calls to a conditional method are either included or omitted depending on whether this symbol is defined at the point of the call. If the symbol is defined, the call is included; otherwise, the call (including evaluation of the receiver and parameters of the call) is omitted.
How can you verify it? Fire up the developer command prompt, type ildasm <enter>
and open the relevant dlls/exes. Check out the caller and the called [Conditional]
methods. You'll see the called methods have the extra IL with .custom instance
, and the caller lines are omitted where you would expect. Try it on a console application with the code below.
Why? It makes conditional calls simpler in some cases than using #if
. See 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?");
}
}
And in the corresponding MSIL, VerboseEmit
is included, but not called from 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
Bonus points. Look at the console output and the MSIL for this (modify the Emit methods accordingly):
static void Main(string[] args)
{
int callCount = 0;
AlwaysEmit(++callCount);
VerboseEmit(++callCount);
DebugEmit(++callCount);
Console.WriteLine("Call count = " + callCount);
Console.ReadLine();
}