Wednesday, August 30, 2006 10:04 PM bart

Conditional compilation in C# - explaining System.Diagnostics.ConditionalAttribute

Introduction

Conditional compilation is one of the much unknown powerful features that are available in .NET and more specifically in the C#, VB.NET and J# compilers. In this post, I'd like to show you how to take benefit from this feature, applied to C#.

An example

using System;
using
System.Diagnostics;

class
Program
{
   static void Main(string
[] args)
   {
      DebugLog(
"Before loop"
);

      for (int
i = 0; i < 10; i++)
         Console
.WriteLine(i);

      DebugLog(
"After loop"
);
   }

   [
Conditional("DEBUG")]
   private static void DebugLog(string
s)
   {
      Console
.WriteLine(s);
   }
}

First compile the program using one of the following options:

  1. In Visual Studio 2005 with the active configuration set to Debug.
  2. On the command-line using csc /define:DEBUG Program.cs. (Note: this is not the same as csc /debug+ Program.cs)
  3. Add #define DEBUG in the program code.

When you execute the program, you'll see:

Before loop
0
1
2
3
4
5
6
7
8
9
After loop

In the IL code you'll see something like this:

IL_0000: nop
IL_0001: ldstr "Before loop"
IL_0006: call void Program::DebugLog(string)
IL_000b: nop
IL_000c: ldc.i4.0
IL_000d: stloc.0
IL_000e: br.s IL_001b
IL_0010: ldloc.0
IL_0011: call void [mscorlib]System.Console::WriteLine(int32)
IL_0016: nop
IL_0017: ldloc.0
IL_0018: ldc.i4.1
IL_0019: add
IL_001a: stloc.0
IL_001b: ldloc.0
IL_001c: ldc.i4.s 10
IL_001e: clt
IL_0020: stloc.1
IL_0021: ldloc.1
IL_0022: brtrue.s IL_0010
IL_0024: ldstr "After loop"
IL_0029: call void Program::DebugLog(string)
IL_002e: nop
IL_002f: ret

Nothing special going on despite the declaration of the [Conditional("DEBUG")] attribute. However, let's compile the same application now as a release build:

  1. In Visual Studio 2005 with the active configuration set to Release.
  2. On the command-line using csc Program.cs.

Now the output will be:

0
1
2
3
4
5
6
7
8
9

and the corresponding IL is:

IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: br.s IL_0010
IL_0005: ldloc.0
IL_0006: call void [mscorlib]System.Console::WriteLine(int32)
IL_000b: nop
IL_000c: ldloc.0
IL_000d: ldc.i4.1
IL_000e: add
IL_000f: stloc.0
IL_0010: ldloc.0
IL_0011: ldc.i4.s 10
IL_0013: clt
IL_0015: stloc.1
IL_0016: ldloc.1
IL_0017: brtrue.s IL_0005
IL_0019: ret

So, no single call to the DebugLog method was emitted in the Main's code. Powerful isn't it? The coolest thing about all this is that you don't have to decorate your code using a bunch of #if statements, like this:

   static void Main(string[] args)
   {
#if DEBUG
      DebugLog(
"Before loop"
);
#endif

      for (int
i = 0; i < 10; i++)
         Console
.WriteLine(i);

#if DEBUG
      DebugLog("After loop");
#endif
   }

One advantage of the latter approach might be that you can also #if DEBUG ... #endif the DebugLog method itself. Using conditional compilation the DebugLog method will get compiled no matter what.

A few remarks to conclude:

  • The ConditionalAttribute can only be applied to methods and to attribute classes (that is: classes that derive from System.Attribute). Thus property and event accessors can't be decorated using this attribute.
  • The Debug and Trace classes of the .NET Framework use the ConditionalAttribute. So you don't have to worry about any performance hit whatsoever when you call various methods of these classes as a debugging aid. These calls just won't make it in the release build (or better: non-debug builds), e.g.:

    [Conditional("DEBUG")]
    public static void Assert (
        bool condition
    )

  • The C++ compiler doesn't support the ConditionalAttribute; you'll have to rely on #if conditionals to include/exclude debugging code.
  • The ConditionalAttribute allows multiple decorations per method (or attribute class):

    [AttributeUsageAttribute(AttributeTargets.Class|AttributeTargets.Method, AllowMultiple=true)]

    Therefore you can do things such as (cf. section 17.4.2.2 of the C# specification):

       [Conditional("ALPHA")]
       [Conditional("BETA")]
       private static void DebugLog(string
    s)
  • Attribute decorations are emitted to the metadata of a class. Because of this, you can get ConditionalAttribute working across languages and assemblies. An example of a class definition in C#:

    public static class Helper
    {
       [Conditional("DEBUG")]
       public static void DebugLog(string
    s)
       {
          Console
    .WriteLine(s);
       }
    }


    and its usage in VB:

    Class Program
       Shared Sub Main()
          Helper.DebugLog(
    "Before loop"
    )

          Dim As Integer
          For
    i = 0 To 10
             Console
    .WriteLine(i)

          Helper.DebugLog(
    "After loop"
    )
       End Sub
    End Class

    This will yield the same result as the C# only example, because the VB compiler can find out (using the metadata of class Helper) that calls to DebugLog should only be made when a DEBUG build is made.

References

Enjoy!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under: , ,

Comments

# re: Conditional compilation in C# - explaining System.Diagnostics.ConditionalAttribute

Thursday, August 31, 2006 10:45 AM by Ramon Smits

I use this attribute *a lot*. The only thing is that when you inspect your assembly with for example fxcop that the method is still compiled and that fxcop says that the method isn't used by any code. This is weird behaviour because the method isn't of any so shouldn't be compiled at all.

# Compila????o condicional, sem #if yyy &raquo; KaitoDev

Tuesday, March 09, 2010 5:58 AM by Compila????o condicional, sem #if yyy » KaitoDev

Pingback from  Compila????o condicional, sem #if yyy &raquo; KaitoDev

# Conditional Methods in C# &laquo; Jeremy Branham&#039;s Blog

Wednesday, April 20, 2011 10:58 AM by Conditional Methods in C# « Jeremy Branham's Blog

Pingback from  Conditional Methods in C# &laquo; Jeremy Branham&#039;s Blog

# Bart diagnostics | Testicularradi

Saturday, October 01, 2011 11:33 AM by Bart diagnostics | Testicularradi

Pingback from  Bart diagnostics | Testicularradi

# Can I do a conditional compile based on compiler version? - Just just easy answers

Pingback from  Can I do a conditional compile based on compiler version? - Just just easy answers