#if (C# Reference)

When the C# compiler encounters an #if directive, followed eventually by an #endif directive, it compiles the code between the directives only if the specified symbol is defined. Unlike C and C++, you cannot assign a numeric value to a symbol. The #if statement in C# is Boolean and only tests whether the symbol has been defined or not. For example:

#if DEBUG
    Console.WriteLine("Debug version");
#endif

You can use the operators == (equality) and != (inequality) only to test for true or false. True means the symbol is defined. The statement #if DEBUG has the same meaning as #if (DEBUG == true). You can use the operators && (and), || (or), and ! (not) to evaluate whether multiple symbols have been defined. You can also group symbols and operators with parentheses.

Remarks

#if, along with the #else, #elif, #endif, #define, and #undef directives, lets you include or exclude code based on the existence of one or more symbols. This can be useful when compiling code for a debug build or when compiling for a specific configuration.

A conditional directive beginning with a #if directive must explicitly be terminated with a #endif directive.

#define lets you define a symbol. By then using the symbol as the expression passed to the #if directive, the expression evaluates to true.

You can also define a symbol with the -define compiler option. You can undefine a symbol with #undef.

A symbol that you define with -define or with #define doesn't conflict with a variable of the same name. That is, a variable name should not be passed to a preprocessor directive, and a symbol can only be evaluated by a preprocessor directive.

The scope of a symbol created with #define is the file in which it was defined.

The build system is also aware of predefined preprocessor symbols representing different target frameworks. They're useful when creating applications that can target more than one .NET implementation or version.

Target Frameworks Symbols
.NET Framework NET20, NET35, NET40, NET45, NET451, NET452, NET46, NET461, NET462, NET47, NET471, NET472
.NET Standard NETSTANDARD1_0, NETSTANDARD1_1, NETSTANDARD1_2, NETSTANDARD1_3, NETSTANDARD1_4, NETSTANDARD1_5, NETSTANDARD1_6, NETSTANDARD2_0
.NET Core NETCOREAPP1_0, NETCOREAPP1_1, NETCOREAPP2_0, NETCOREAPP2_1, NETCOREAPP2_2

Other predefined symbols include the DEBUG and TRACE constants. You can override the values set for the project using #define. The DEBUG symbol, for example, is automatically set depending on your build configuration properties ("Debug" or "Release" mode).

Examples

The following example shows you how to define a MYTEST symbol on a file and then test the values of the MYTEST and DEBUG symbols. The output of this example depends on whether you built the project on Debug or Release configuration mode.

#define MYTEST
using System;
public class MyClass
{
    static void Main()
    {
#if (DEBUG && !MYTEST)
        Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && MYTEST)
        Console.WriteLine("MYTEST is defined");
#elif (DEBUG && MYTEST)
        Console.WriteLine("DEBUG and MYTEST are defined");  
#else
        Console.WriteLine("DEBUG and MYTEST are not defined");
#endif
    }
}

The following example shows you how to test for different target frameworks so you can use newer APIs when possible:

public class MyClass
{
    static void Main()
    {
#if NET40
        WebClient _client = new WebClient();
#else
        HttpClient _client = new HttpClient();
#endif
    }
    //...
}

See also