In the examples you've seen so far, you've compiled your entire program whenever you compiled any of it. At times, however, you might want to compile only parts of your programfor example, depending on whether you are debugging or building your production code.
Before your code is compiled, another program called the preprocessor runs and prepares your program for the compiler. The preprocessor examines your code for special preprocessor directives, all of which begin with the pound sign (#). These directives allow you to define identifiers and then test for their existence.
#define DEBUG defines a preprocessor identifier, DEBUG. Although other preprocessor directives can come anywhere in your code, identifiers must be defined before any other code, including using statements.
You can test whether DEBUG has been defined with the #if statement. Thus, you can write:
#define DEBUG //... some normal code - not affected by preprocessor #if DEBUG // code to include if debugging #else // code to include if not debugging #endif //... some normal code - not affected by preprocessor
When the preprocessor runs, it sees the #define statement and records the identifier DEBUG. The preprocessor skips over your normal C# code and then finds the #if - #else - #endif block.
The #if statement tests for the identifier DEBUG, which does exist, and so the code between #if and #else is compiled into your programbut the code between #else and #endif is not compiled. That code does not appear in your assembly at all; it is as if it were left out of your source code.
Had the #if statement failedthat is, if you had tested for an identifier that did not existthe code between #if and #else would not be compiled, but the code between #else and #endif would be compiled.
Undefine an identifier with #undef. The preprocessor works its way through the code from top to bottom, so the identifier is defined from the #define statement until the #undef statement, or until the program ends. Thus if you write:
#define DEBUG #if DEBUG // this code will be compiled #endif #undef DEBUG #if DEBUG // this code will not be compiled #endif
the first #if will succeed (DEBUG is defined), but the second will fail (DEBUG has been undefined).
There is no switch statement for the preprocessor, but the #elif and #else directives provide great flexibility. The #elif directive allows the else-if logic of "if DEBUG then action one, else if TEST then action two, else action three":
#if DEBUG // compile this code if debug is defined #elif TEST // compile this code if debug is not defined // but TEST is defined #else // compile this code if neither DEBUG nor TEST // is defined #endif
In this example, the preprocessor first tests to see if the identifier DEBUG is defined. If it is, the code between #if and #elif will be compiled, and the rest of the code until #endif will not be compiled.
If (and only if) DEBUG is not defined, the preprocessor next checks to see if TEST is defined. Note that the preprocessor will not check for TEST unless DEBUG is not defined. If TEST is defined, the code between the #elif and the #else directives will be compiled. If it turns out that neither DEBUG nor TEST is defined, the code between the #else and the #endif statements will be compiled.
The #region preprocessor directive marks an area of text with a comment. The principal use of this preprocessor directive is to allow tools such as Visual Studio .NET to mark off areas of code and collapse them in the editor with only the region's comment showing.
For example, when you create a Windows application (covered in Chapter 13), Visual Studio .NET creates a region for code generated by the designer. When the region is expanded, it looks like Figure 3-1. (Note: I've added the rectangle and highlighting to make it easier to find the region.)
You can see the region marked by the #region and #endregion preprocessor directives. When the region is collapsed, however, all you see is the region comment (Windows Form Designer generated code), as shown in Figure 3-2.