The C Preprocessor Directives

 
Chapter 4 - Advanced C# Topics
bySimon Robinsonet al.
Wrox Press 2002
  

The C# Preprocessor Directives

Besides the usual keywords, most of which we 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 are all distinguished by beginning with the # symbol.

C++ developers will recognize the preprocessor directives as something that plays an important part in C and C++. However, there aren't as many preprocessor directives in C#, and they are not used as often. C# provides other mechanisms that achieve the same effect as many of the C++ directives. Also, note that C# doesn't actually have a separate preprocessor in the way that C++ does. The so-called preprocessor directives are actually handled by the compiler. Nevertheless, C# retains the name 'preprocessor directive' because these commands give the impression of a preprocessor.

We will briefly cover the purposes of the preprocessor directives here.

#define and #undef

#define is used like this:

   #define DEBUG   

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 just exists while the compiler is compiling the code.

#undef does the opposite , and removes the definition of a symbol:

   #undef DEBUG   

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 need 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 #if , it becomes very powerful.

Incidentally, you might notice some changes from the usual C# syntax. Preprocessor directives are not terminated by semi-colons, and are normally the only command on a line. That's because 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 the next command is on the next line.

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

These directives inform the compiler whether or not to compile a block of code. Consider this method:

   int DoSomeWork(double x)     {     // do something     #if DEBUG     Console.WriteLine("x is " + x);     #endif     }   

This code will compile as normal, except for the Console.WriteLine() method call that is contained inside the #if clause. This line will only be executed if the symbol DEBUG has been defined by a previous #define directive. When the compiler finds the #if directive, it checks to see if the symbol concerned exists, and only compiles the code inside the #if clause if the symbol does exist. Otherwise, the compiler simply ignores all the code until it reaches the matching #endif directive. Typical practice is to define the symbol DEBUG while you are debugging, and have various bits of debugging related code inside #if 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 the intuitively obvious meanings, and it is also possible to nest #if blocks:

   #define ENTERPRISE     #define W2K     // further on in the file     #if ENTERPRISE     // do something     #if W2K     // some code that is only relevant to enterprise     // edition running on W2K     #endif     #elif PROFESSIONAL     // do something else     #else     // code for the leaner version     #endif   

Note that, unlike the situation in C++, using #if is not the only way to conditionally compile code. C# provides an alternative mechanism via the Conditional attribute, which we will explore in the next section.

#if and #elif support a limited range of logical operators too, using the operators ! , == , != , and . A symbol is considered to be true if it exists and false if it doesn't. For example:

   #if W2K && (ENTERPRISE==false)   // if W2K is 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, then it will display whatever text appears after the #warning to the user , after which compilation continues. If it 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, and you can also use the #warning statements to remind yourself to do something:

   #if DEBUG && RELEASE     #error "You've defined DEBUG and RELEASE simultaneously! "     #endif     #warning "Don't forget to remove this line before the boss tests the code! "     Console.WriteLine("*I hate this job*");   

#region and #endregion

The #region and #endregion directives are used to mark that a certain block of code is to be treated as a single block with a given name, like this:

   #region Member Field Declarations     int x;     double d;     Currency balance;     #endregion   

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 lay out your code better on the screen. We will see how this works in Chapter 6, when we look at Visual Studio. NET.

#line

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. Its main use occurs if you are coding in conjunction with some other package that alters the code you are typing in before sending it to the compiler, since this will mean 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:

   #line 164 "Core.cs"   // we happen to know this is line 164 in the file     // Core.cs, before the intermediate     // package mangles it.   // later on   #line default      // restores default line numbering   
  


Professional C#. 2nd Edition
Performance Consulting: A Practical Guide for HR and Learning Professionals
ISBN: 1576754359
EAN: 2147483647
Year: 2002
Pages: 244

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net