In .NET the System.Diagnostics.ConditionalAttribute can be used for conditional compilation. A method can be marked with this attribute as follows

using System.Diagnostics;
static class Logger
{
    [Conditional("TRACE")]
    static public void Info(string message)
    {
        Console.WriteLine(message);
    }
}

This means that the Logger.Trace is compiled into IL only when the TRACE sumbol is defined either in the command line build as /define:TRACE or in the VS IDE in Project->Properties->Build->General->Conditional Compilation symbols. This makes ConditionalAttribute an excellent choice for logger and debug utilities. For example the logger can have the Info and verbose methods marked with [Conditional("Debug")] and hence they do not add to the burden of in release bits. Conditional attributes is a much better choice than #ifdefs as they are always syntax verified.

However, it brought up a very interesting discussion recently. Consider the following code

MyClass mc = new MyClass();
Logger.Info(mc.ToString());

The question is does the mc.ToString evaluate when TRACE is not defined?? Or in other words are parameters of conditionally compiled methods evaluated? If it does then even for release bits overhead starts creeping in. The good news is that it doesn't. An easy way to verify is to override MyClass::ToString and put in a Console.WriteLine into it. This method will not be called if TRACE is not defined. Or you can just use ILDASM or .NET Reflector to see that the method call doesn't even go into the IL and hence there can be no overhead.