Preprocessor Directives

Preprocessor directives are instructions to the C# compiler to perform some action during compilation. There is no equivalent functionality in Java. The term preprocessor directive is misleading because the compilation process does not include a preprocessing step, but the term is a legacy of C/C++ and has remained to avoid confusion.

Preprocessor directives are used to

  • Conditionally include and exclude sections of code during compilation.

  • Force the compiler to issue warnings and errors.

  • Group related sections of source code.

  • Alter the line number and file-name references displayed by the compiler for errors and warnings.

Preprocessor directives are preceded by the # symbol and must be the only command on a line. They are not terminated with semicolons.

#define and #undef

The #define and #undef directives are used to define and undefine conditional compilation symbols. The #if directive, described in the next section, uses these symbols to control conditional compilation. The following example demonstrates the use of the #define and #undef directives:

#define release
#define win2000
#undef release

This example defines the symbols release and win2000. It then undefines release. Only the win2000 symbol is defined at the end of the code fragment. Unless explicitly defined, a symbol is undefined. Once a symbol is defined, it remains defined until it is explicitly undefined or the end of the file is reached. Any preprocessor definition, either a #define or an #undef, must be placed before any code, including a using directive, or else a compiler error will occur. Symbols can also be defined using the /d switch during compilation. Compiler options are discussed in Chapter 3.

#if, #elif, #else, and #endif

The #if, #elif, #else, and, #endif directives provide conditional compilation based on the definition of symbols as described in the preceding section. The structure and behavior of the #if directive are the same as those of a normal if conditional statement in C# and Java except that the #if directive is terminated with an #endif directive. The compiler evaluates each #if and #elif clause until it finds one that evaluates to true. It then evaluates the enclosed code for possible inclusion in compilation.

If no #if or #elif clause evaluates as true and there is an #else clause, the enclosed code will be evaluated. The following example determines the content of the string welcomeString during compilation based on which preprocessor symbol is defined:

string welcomeString = null;

#if win2000    //compiling for Windows 2000 platform
    welcomeString = "Welcome to Windows 2000";
#elif win98    //compiling for Windows 98 platform
    welcomeString = "Welcome to Windows 98";
#elif win95    //compiling for Windows 95 platform
    welcomeString = "Welcome to Windows 95";
#else          //compiling for all other platforms
    welcomeString = "Welcome to Windows";
#endif

The test condition supports logical operators and brackets to enable complex conditions to be represented. The supported logical operators available are described in Table 6-3.

Table 6-3. Supported Logical Operators for #if Clause Conditionals

Operator

Example

Comment

==

#if win2000 == true

Equality. Expression will evaluate to true if the win2000 symbol has been defined.

!=

#if win2000 != true

Inequality. Expression will evaluate to true if the win2000 symbol has not been defined.

&&

#if win2000 && release

Logical AND. Expression will evaluate to true only if both the win2000 AND release symbols have been defined.

||

#if win2000 || release

Logical OR. Expression will evaluate to true if either the win2000 OR release symbol has been defined.

()

#if (win2000 && release) || win95

Parentheses. Used to group expressions. Expression will evaluate to true if either the win2000 AND release symbols are defined OR the win95 symbol is defined.

It’s possible to nest #if directives, but the overly complex use of conditional compilation directives will make code difficult to understand and debug.

More Info

An alternative approach for conditional compilation is the use of the Conditional attribute, which is discussed in the System.Diagnostics.ConditionalAttribute section earlier in this chapter.

#region and #endregion

The #region and #endregion directives mark a block of code. These directives have no meaning in the context of C# but are implemented for the benefit of developers and tools. For example:

#region SomeClass related code
public class SomeClass{
    #region SomeClass data members
    // Declare fields and constants
    #endregion

    #region SomeClass function members
    // Declare methods, properties and operators
    #endregion
}
#endregion

The Visual Studio .NET development environment uses these directives to allow a developer to collapse and expand blocks of code to improve readability during development.

Note the following:

  • A compiler error will occur if there are not enough matching #region and #endregion directive pairs to close all regions correctly.

  • Overlapping regions cannot be created. There is no way to map an #endregion to a specific #region directive.

  • The comments on the #region and matching #endregion directives are not related and can be different.

#warning and #error

The #warning and #error directives explicitly generate warnings and errors during code compilation. The message the compiler should display follows the directive. There is no need to place the message in quotation marks. This directive can be used in conjunction with the conditional compilation directives already discussed. For example:

#warning TODO: Need to rework method to be more efficient

#if (debug && release)
    #error Multiple build version symbols specified
#endif

This code will always generate the TODO... warning when compiled but will generate the Multiple build... error only if both the debug and release symbols have been defined using #define or the /d compiler flag.

#line

The purpose of the #line directive is to modify the line numbers and source file names reported in warnings and errors generated by the compiler. This directive is aimed at tools that generate C# code and will not be useful to most developers. In the following example:

#line 100 "MyFileName"

The compiler will consider the line immediately following this directive to be line 100 and the file name to be MyFileName. These settings will remain in force until the end of the file or until a new #line directive is encountered. Subsequent instances of the #line directive override previous #line directives.

To undo the effects of any existing #line directives, use the following directive:

#line default
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset