다음을 통해 공유


C#: Conditional Attribute and #if Directive

In this article, let’s see the use of Conditional attribute and how it differs from #if directive.

It’s always best to go by an example. Consider the following:

Example 1

using System;
using System.Diagnostics;
 
namespace ConditionalAttributeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            SayHello1();
        }
 
        static void SayHello1()
        {
            Console.WriteLine("Hello World 1");
        }
    }
}

It’s more than simple. If we run this, “Hello World 1” will be printed on the console. Now imagine that we want the method to be run only when a given condition satisfies. You can think of a variety of ways to achieve that. But here the condition is based on a compilation symbol (DEBUG, etc.).

Now let’s modify the code adding a compilation symbol named SOMETHING.

Example 2

#define SOMETHING
 
using System;
using System.Diagnostics;
 
namespace ConditionalAttributeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            SayHello1();
        }
 
        [Conditional("SOMETHING")]
        static void SayHello1()
        {
            Console.WriteLine("Hello World 1");
        }
    }
}

Now if we run this application, again "Hello World 1" will be printed on the console. And if we commented out the first line "#define SOMETHING", SayHello1() method will not get called thus the application will not print anything. It’s basically because the condition fails.

Now as you might be aware, we can do the same using an #if directive. Let’s modify the code to use #if.

Example 3

#define SOMETHING
 
using System;
 
namespace ConditionalAttributeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
#if SOMETHING
            SayHello1();
#endif
        }
 
        static void SayHello1()
        {
            Console.WriteLine("Hello World 1");
        }
    }
}

This will print “Hello World 1” and if we commented out the SOMETHING symbol, SayHello1() method will not get called. It’s basically just as in Example 2. So now you must be wondering what’s the difference between Conditional attribute and #if directive. Let’s move on.

The first difference is when you use #if, the code which is wrapped between #if and #endif, will only get compiled if the condition next to #if is true. Basically, you can write almost anything within the #if and #endif and as long the condition is false. It will not throw any compilation errors (even if there are any).

The second and most important difference is, how IL is emitted in both these scenarios. When you use #if, the IL for code within the #if and #endif, will only get emitted if the given condition is true. Where as when you use Conditional attribute, IL will be emitted based on the method calls. Confused? Let’s go by the example. Let's modify the code to use both a Conditional attribute and #if, and then let’s compare the IL which gets generated.

Example 4

#define SOMETHING
 
using System;
using System.Diagnostics;
 
namespace ConditionalAttributeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
#if SOMETHING
            SayHello1();
#endif
            SayHello2();
        }
 
        static void SayHello1()
        {
            Console.WriteLine("Hello World 1");
        }
 
        [Conditional("SOMETHING")]
        static void SayHello2()
        {
            Console.WriteLine("Hello World 2");
        }
    }
}

Now after compiling the code, let’s see what the generated IL is (here we are using ildasm.exe).

https://lh3.googleusercontent.com/-dCGFNaJ_fLE/V87beuSTQTI/AAAAAAAAENo/0rESOYoatSU/image_thumb%25255B15%25255D.png?imgmax=800
ildasm.exe

Here let’s see what we have for Main() method.

https://lh3.googleusercontent.com/-btqfPJzugHA/V87bgHnaswI/AAAAAAAAENw/1NhCl5rALd4/image_thumb%25255B13%25255D.png?imgmax=800
IL for Main()

You can see that IL is generated for two method calls SayHello1() and SayHello2(). Now let’s comment out the compilation symbol SOMETHING, compile the code and examine the emitted IL back again.

https://lh3.googleusercontent.com/-Ncc6z-Jwbpo/V87bhi8y43I/AAAAAAAAEN4/dYQ_i1bfjo4/image_thumb%25255B12%25255D.png?imgmax=800
IL for Main()

Now there is nothing on SayHello1() and SayHello2() method calls in Main() method. Still no difference. Let’s come to this later in the post again.

Now let’s modify the code further as below.

Example 5

//#define SOMETHING
 
using System;
using System.Diagnostics;
 
namespace ConditionalAttributeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
#if SOMETHING
            SayHello1();
#endif
            SayHello2();
        }
 
        static void SayHello1()
        {
            Console.WriteLine("Hello World 1");
            SayHello3();
        }
 
        static void SayHello2()
        {
            Console.WriteLine("Hello World 2");
            SayHello3();
        }
 
        [Conditional("SOMETHING")]
        static void SayHello3()
        {
            Console.WriteLine("Hello World 3");
        }
    }
}

Take note that we have the first line commented out (otherwise, there won’t be any difference). Now let’s compile and examine the IL on Main() method back again.

https://lh3.googleusercontent.com/-ADiCWoXhcvY/V87bjFoQUOI/AAAAAAAAEOA/Ibl6I_H4_YM/image_thumb%25255B11%25255D.png?imgmax=800
IL for Main()

And now, you should be able to see a clear difference. There is nothing on SayHello1() method call, but you can see the IL code for SayHello2() method call.

The reason is, now the call to our conditional method (SayHello3()) is done through the intermediate method (SayHello2()). And you can see, even if we use the same approach in #if scenario, but still no IL was generated for that.

And now the reason for IL code to be the same on Example 4 is, in that case we were directly calling a conditional method (SayHello2()) from the Main() method. And since the condition is false, the compiler knows it can’t call the SayHello2(). Because of that no IL code is generated for conditional (SayHello2()) method call.

Hope this article gives you a clear understanding on Compilation attributes and its difference between #if directive.

Happy Coding.