Besides ‘the usual keywords, most of which you have now encountered, C# also includes a number of commands that are known as preprocessor directives. These commands never actually get translated.to any commands in your executable code, but instead they affect aspects of the compilation process. For example, you can use preprocessor directives to prevent the compiler from compiling certain portions of your code. You might do this if you are planning to release two versions of the code – a basic version and an enterprise version that will have more features. You could use preprocessor directives to prevent the compiler from compiling code related to the additional features when you are compiling the basic version of the software. Another scenario is that you might have written bits of code that are intended to provide you with debugging information. You probably don’t want those portions of code compiled when you actually ship the software.
The preprocessor directives ere all distinguished by beginning with the # symbol.
C++ developers will recognize the preprocessor directitxs as something that plays 11/1 important part in C and C++. However, there arl’l/’t lIS many preprocessor direct hoesill C#, and they are not used as often. C# provides other mechanisms, such as custom attributes, that actual some of the same effects as C++ directives. Also, note that C# doesn’t actually hat:e a separate preprocessor in the way that C++ does. The 50-called preprocessor directly compiler.. Netertheless, C# retains the name preprocessor directive b.x ;e impression of // preprocessor.
The next sections briefly cover the purposes of the preprocessor directives.
#define and #undef
#define is used like this:
What this does is tell the compiler that a symbol with the given name (in this case :DEBUG)exists. It is a little bit like declaring a variable, except that this variable doesn’t really have a value – it just exists. And this symbol isn’t part of your actual code; it.exists only for the benefit of the compiler, while the compiler is compiling the code, and has no meaning within the C# code itself.
#undef does the opposite, and removes the definition of a symbol:
If the symbol doesn’t exist in the first place, then #undef has no effect. Similarly, .define has no effect if a symbol already exists.
You n~ed to place any #define and #undef directives at the beginning of the C# source file, before any code that declares any objects to be compiled.
#define isn’t much use on its own, but when combined with other preprocessor directives, especially # i f, it becomes very powerful.
Incidentally, you might notice some changes from the //s//a/ C# syntax. Preprocessor directives are not terminated by semicolons and normally constitute the only command on a line. That’s be call for the preprocessor directives, C# abandons its usual practice of requiring commands to be separated by semicolons. If it sees a preprocessor directive, it assumes that the next command is on tile next line.
#lf, #elif, #else, and #endif
These directives inform the compiler whether to compile a block of code. Consider this method:
This code will compile as normal, except for the Console. WriteLine ( ) method call that is contained inside the h f clause. This line will be executed only if the symbol DEBUG has been defined by a previous *define directive. When the compiler finds the hf directive, it checks to see if the symbol concerned exists and compiles the code inside the *if clause only if the symbol does exist. Otherwise, the compiler simply ignores all the code until it reaches the matching Jlendi f directive. Typical practice is to define the symbol DEBUG while you are debugging and have various bits of debugging-related code inside JIi f clauses. Then, when you are close to shipping, you simply comment out the #define directive, and all the debugging code miraculously disappears, the size of the executable file gets smaller, and your end users don’t get confused by being shown debugging information. (Obviously, you would do more testing to make sure your code still works without DEBUG defined.) This technique is very common in C and C++ programming and is known as conditional compilation.
The #elif (=else if) and #else directives can be used in #if blocks and have intuitively obvious meanings. It is also possible to nest #if blocks:
#if and #elif support a limited range.of logical operators too, using the operators!, ==, ! =, and II. A symbol is considered to be true if it exists and false if it doesn’t. For example:
#if W2K&& (ENTERPRISE==false) // if W2Kis defined but ENTERPRISE isn’t
#warning and #error
Two other very useful preprocessor directives are #warning and #error. These will respectively cause a warning or an error to be raised when the compiler encounters them. If the compiler sees a #warning directive, it will display whatever text appears after the #warning to the user, after which compilation continues. Hit encounters a #error directive, it will display the subsequent text to the user as if it were a compilation error message and then immediately abandon the compilation, so no IL code will be generated.
You can use these directives as checks that you haven’t done anything silly with your #define statements; you can also use the #warning statements to remind yourself to do something:
#region and #endregion
The #region and #endregion directives are used to indicate that a certain block of code is to be treated as a single block with a given name, like this:
this doesn’t look that useful by itself; it doesn’t affect the compilation process in any way. However, the real advantage is that these directives are recognized by some editors, including the Visual Studio .NET editor. These editors can use these directives to layout your code better on the screen.
The #line directive can be used to alter the file name and line number information that is output by the compiler in warnings and error messages. You probably won’t want to use this directive that often. It’s most useful when you are coding in conjunction with some other package that alters the code you are typing in before sending it to the compiler. In this situation, line numbers, or perhaps the file names reported by the compiler, won’t match up to the line numbers in the files or the file names you are editing. The #line directive can be used to restore the match. You can also use the syntax #line default to restore the line to the default line numbering:
The #pragma directive can either suppress or restore specific compiler warnings. Unlike command-line options, the #pragma directive can be implemented on a class or method level, allowing a fine-grained control of what warnings are suppressed and when. The following example disables the “field not used” warning and then restores it after the MyClass class compiles: