Compiling Code Conditionally

Compiling Code Conditionally

Visual C# .NET includes a useful feature known as conditional compilation, which allows you to define methods that will be compiled when specific symbols are defined. Conditional compilation is commonly used with methods that are used only for debug builds. If code is useful only during debugging, conditional compilation can be used to automatically remove the code for release builds.

Prior to the advent of the Microsoft .NET platform, the traditional method of compiling conditionally using C++ or Visual Basic was to explicitly mark the regions of conditional code. The following code is an example of conditional compilation using C++:

// C++, not C#
class BankAccount
{
public:
#ifdef DEBUG
    void DumpAccountDetail()
    {
        // Display account information.
    }
#endif
    
    

};

void HandleTransaction()
{
    BankAccount* account = new BankAccount();
    
    

#ifdef DEBUG
    // Call conditional method.
    account->DumpAccountDetail();
#endif
}

The C++ preprocessor scans the source file and removes the conditional code in the initial steps of compiling a C++ project. The disadvantage of using the preprocessor is that both the method and the method caller must mark the code for conditional compilation. Additionally, the syntax is awkward, especially when client code must invoke a number of conditional methods.

When developing code with conditional methods using Visual C# .NET, you can take advantage of a much simpler and more flexible conditional compilation technique—the Conditional compilation attribute, which is part of the System.Diagnostics namespace. To mark a method as conditionally compiled, apply the Conditional attribute to the method, as shown here:

[Conditional("DEBUG")]
public void DumpAccountDetail()
{
    // Display account information.
}

In the preceding code, the DumpAccountDetail method is compiled only when the DEBUG symbol is defined. By default, a Visual C# project defines DEBUG and TRACE for debug builds and defines TRACE only for release builds. There are two ways to define a new symbol. The first method is to simply define the conditional compilation symbol at the top of any source file that calls the conditionally compiled method, as shown here:

#define TEST

The second way to define a new conditional compilation symbol for a project is through the Build property page for the project, as shown in Figure 9-1. The Property Pages dialog box is displayed by right-clicking the project icon in Solution Explorer. Individual property pages are displayed by selecting a node in the tree control in the left pane; the Build property page is located in the Configuration Properties folder.

Figure 9-1.
The Build property page for a project, on which new symbols are defined.

The project’s conditional compilation symbols are separated by semicolons. You can append new symbols to the existing items, but you must separate the symbols with semicolons. Before making changes to the property page, make sure that the changes will be applied to the proper project configuration. In the Configuration drop-down list, you can select the currently active configuration or any of the other build configurations or you can apply a change to all build configurations.

A method can be tagged with multiple Conditional compilation attributes, as shown here:

[Conditional("DEBUG")]
[Conditional("TEST")]
public void SetValue(int newValue)
{
    
    

}

This method will be compiled if either the DEBUG or the TEST symbol is defined.

Conditional methods are subject to the following restrictions:

  • The Conditional attribute can’t be applied to a method within an interface—it can be applied only to a method implementation. Methods declared in an interface and implemented in a class or structure can’t be annotated with the Conditional attribute.

  • The Conditional attribute can’t be applied to a method marked with the override modifier. It can be applied to a virtual method, which has the effect of conditionally compiling all overridden versions of the method.

  • The Conditional attribute can be applied only to methods with a void return type.

In most cases, it doesn’t make sense to conditionally compile a method with a non-void return type. Because the return value is often used to set the value of variables, if conditional compilation of these methods were allowed, it would often result in variables left unassigned, as shown here:

static void Main(string[] args)
{
    string secretWord = GetSecretWord();
    Console.WriteLine("The secret word is {0}", secretWord);
}

// Won't compile due to non-void return type
[Conditional("TEST")]
public static string GetSecretWord()
{
    return "Fulminate";
}

You can work around this restriction by conditionally compiling methods that mark their parameters with the out or ref keyword. Keep in mind that when a conditional method with an out or a ref parameter isn’t compiled, as might be the case in the following code, a compiler error can occur if a variable is used before it’s assigned a value.

[Conditional("TEST")]
public void GetSecretWord(out string secretWord)
{
    secretWord = "Fulminate";
}


Part III: Programming Windows Forms