C# Preprocessor Directives

Introduction

This article has been excerpted from the book "The Complete Visual C# Programmer's Guide" by the Authors of C# Corner.

The C# preprocessor is a macro processor that the C# compiler automatically uses to transform your program before actual compilation. The C# preprocessor allows you to define macros, which are brief abbreviations for longer constructs.

A preprocessor directive must be the only instruction on a line. Preprocessing directives start with #, followed by an identifier that is the directive name. For example, #define is the directive. White space is allowed before and after the #.

The C# language's preprocessor directives are as follows.

  • #define
  • #if
  • #else
  • #elif
  • #endif
  • #undef
  • #warning
  • #error
  • #line
  • #region
  • #endregion

The main uses of directives include conditional compilation, line control, error and warning reporting, and region and end region. Let's take a preliminary look at each.

  • Conditional compilation: Using special preprocessing directives, you can include or exclude parts of the program according to various conditions.
  • Line control: If you use a program to combine or rearrange source files into an intermediate file that is then compiled, you can use line control to inform the compiler where each source line originated.
  • Error and warning reporting: The directive #error causes the preprocessor to report a fatal error. The directive #warning is much like the directive #error, but it causes the preprocessor to issue a warning and continue preprocessing.
  • Region and end region: These new directives, not found in C and C++, allow you to specify a block of code that you can expand or collapse.

Before further explaining the meaning of each preprocessor directive, it's helpful to see how to define these directives. Listings 5.59 and 5.60 illustrate the two methods for defining directives: in your C# program and on the command line at compile-time.

Listing 5.59. Define Example 1

#define TEST
using System;
public class MyClass
{
    public static void Main()
    {
#if (TEST)
        Console.WriteLine("TEST is defined");
#else
        Console.WriteLine("TEST is not defined");
#endif
        Console.ReadLine();
    }
}

This first example of directive defining produces the output in Figure 5.17.

Screen Output Generated

Figure 5.17. Screen Output Generated from Listing 5.59

Illustrating the second means of defining preprocessor directives is the code in Listing 5.60, which you compile using the /define: TEST compiler option.

Listing 5.60. Define Example 2

using System;
public class MyClass
{
    public static void Main()
    {
#if (TEST)
        Console.WriteLine("TEST is defined");
#else
        Console.WriteLine("TEST is not defined");
#endif
        Console.ReadLine();
    }
}

This code generates the output in Figure 5.18.

code generates

Figure 5.18. Screen Output Generated from Listing 5.60

#define Directive

The #define directive allows you to define a symbol that, when used as the expression passed to the #if directive, causes the expression to evaluate to true.

#undef Directive

The #undef directive allows you to undefine a symbol that, when used as the expression in an #if directive, causes the expression to evaluate to false.

For the #undef example in Listing 5.61, compile the code with the /D: DEBUG compiler option.

Listing 5.61. Undef Example

#undef DEBUG
using System;
public class MyClass
{
    public static void Main()
    {
#if DEBUG
        Console.WriteLine("DEBUG is defined");
#else
        Console.WriteLine("DEBUG is not defined");
#endif
        Console.ReadLine();
    }
}

The code snippet produces the screen output shown in Figure 5.19.

code snippet produces

Figure 5.19.Screen Output Generated from Listing 5.61

#if Directive

The #if directive allows you to conditionally choose to include code if the expression is true and, in its simplest form consists of the following elements.

#if expression
    controlled text
#endif /* expression */

The comment following the #endif is not required, but it is a good practice because it helps people match the #endif to the corresponding #if. Such comments should always be used, except in short conditionals that are not nested.

#else Directive

The #else directive can be added to a conditional directive to provide alternative text to be used if the condition is false. It consists of the following elements.

#if expression
    text-if-true
#else /* Not expression */
    text-if-false
#endif /* Not expression */

If the expression is nonzero and thus the text-if-true element is active, then #else acts like a failing conditional, and the text-if-false element is ignored.

#elif Directive

Like #else, the #elif directive goes in the middle of an #if-#endif pair and subdivides it. It does not require a matching #endif of its own. Like #if, the #elif directive includes an expression to be tested.

The text following the #elif is processed only if the original #if condition fails and the #elif condition succeeds. More than one #elif can go in the same #if-#endif group. In the case of multiple #elif directives, the text after each successive #elif is processed only if all previous #elif directives have failed. You may use the #else directive after any number of #elif directives, but an #elif directive may not follow #else. Listing 5.62 illustrates the use of the #if-#elif-#else preprocessor structure.

Listing 5.62. If-Elif-Endif Example

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

If-Elif-Endif

Figure 5.20. Screen Output Generated from Listing 5.62

#endif Directive

The #endif directive specifies the end of a conditional directive that begins with the #if directive.

#error Directive

The directive #error causes the preprocessor to report a fatal error. The tokens forming the rest of the line following #error are used as the error message.

Listing 5.6. Error Example

#define DEBUG
public class MyClass
{
    public static void Main()
    {
#if DEBUG
#error DEBUG is defined
#endif
    }
}

#warning Directive

The directive #warning is much like the directive #error, but it causes the preprocessor to issue a warning and continue preprocessing. The tokens following #warning are used as the warning message. Listing 5.64 presents an example.

Listing 5.64. Warning Example

#define DEBUG
public class MyClass
{
    public static void Main()
    {
#if DEBUG
#warning DEBUG is defined
#endif
    }
}

#line Directive
The #line directive (an example of which is shown in Listing 5.65) specifies the original line number and source file name for subsequent input in the current preprocessor input file.
Listing 5.65. Line.cs, Line Example

using System;
public class MyClass
{
    public static void Main()
    {
#line 100 "abc.sc" // change file name in the compiler output
        int i; // error will be reported on line 100, because of intt
        /* abc.sc(100,3): error CS0246: The type or namespace name 'intt' could not be
        found (are you missing a using directive or an assembly reference?) */
    }
}

#region and #endregion Directives

The #region directive allows you to specify a block of code that you can expand or collapse when using the outlining feature of the Visual Studio Code Editor. Listing 5.66 illustrates the use of #region, paired with the #endregion directive, which marks the end of the block.

Listing 5.66: Region EndRegion Example

#region MyClass definition
public class MyClass
{
    public static void Main()
    {
    }
}
#endregion

Conclusion

I hope this article has helped you in understanding C# Preprocessor Directives. See other articles on the website on .NET

visual C-sharp.jpg

The Complete Visual C# Programmer's Guide covers most of the major components that make up C# and the .net environment. The book is geared toward the intermediate programmer but contains enough material to satisfy the advanced developer.


Similar Articles
C# Corner
C# Corner started as an online community for software developers in 1999.