Compiling Blocks of Code Conditionally






Compiling Blocks of Code Conditionally

Problem

Specific blocks of code will be used only in a certain build configuration for your application. These blocks of code should not be compiled into other builds of the application. You need a way to conditionally compile specific blocks of code based on the type of build.

Solution

There are two devices for allowing or preventing code from being compiled. The first is to use the C# preprocessor directives. The available preprocessor directives are:

	#define
	#undef
	#if
	#elif
	#else
	#endif

The #define and #undef preprocessor directives define and undefine symbols. These symbols are then used by the #if and #elif preprocessor directives to determine whether the blocks of code they wrap are to be compiled. While it is possible to use this device, it is more likely that the second one (discussed next) would be used to support multiple build configurations.

The second device for allowing or preventing code from being compiled is to use the ConditionalAttribute attribute. This allows a method to be compiled based on a defined symbol. This attribute specifies a method as conditionally compiled in the following manner:

	#define TRACE

	…
	[ConditionalAttribute("TRACE")]
	public void TraceHelp(string message)
	{
	    …
	}

The traceHelp method is compiled only when the TRACE preprocessing identifier is defined.

Discussion

The ConditionalAttribute attribute can be used only on methods. It prevents them from being compiled and called at runtime when the preprocessor identifier passed to the ConditionalAttribute constructor is undefined. Properties, indexers, and other members cannot have this attribute.

Another limitation of this attribute is that it can be placed only on a method that returns void. This makes sense, since code that invokes this method doesn't expect a return value and will run successfully whether or not the method is invoked. For example, in the code:

	int retValue = Car.GetModelNumber( );

if the GetModelNumber method is not compiled, then this code will not be able to function correctly.

Along these same lines, a method marked as override cannot be marked with the ConditionalAttribute attribute. However, the virtual method that a method overrides may be marked with the ConditionalAttribute attribute. If the virtual method is marked with this attribute, all methods overriding it are compiled and called based on whether the virtual method is compiled. In other words, the overriding methods are implicitly marked with the same ConditionalAttribute attribute as the virtual method. While this is an interesting side effect of marking the base virtual method, it could cause confusion among developers debugging assemblies that override this method. If they are not familiar with this, the method may simply appear to be missing for them in certain builds depending on what the conditional attribute is specified as. As such, it is not recommended that you do this in practice.

#define and #undef apply only to preprocessor identifiers within a file scope, whereas the /define: compiler option defines preprocessor identifiers for all files in a project. #define and #undef also take precedence over the /define: compiler option. For instance, if the project's /define: compiler option defined TRACE, and one of the files that project contains has the code:

	#undef TRACE

then trACE will be defined for all files except the one containing the #undef TRACE directive.

To set the project's /define: compiler option in Visual Studio .NET, right-click on the project name in the Solution Explorer tool window, then click the Properties menu item. This step will display the Property Pages dialog box for this project. Next, click the Configuration Properties node in the tree on the left side of this dialog box. In the control to the right of this tree, find the line entitled Conditional Compilation Constants. On this line, you may add or remove any preprocessor identifiers that you want.

The #if and #elif directives determine what code within a member is to be compiled. For example:

	public void MyMethod( )
	{
	    #if (TRACE)
	        Method1( );
	    #elif (DEBUG)
	        Method2( );
	    #else
	        Method3( );
	    #endif
	}

MyMethod will call Method1 when trACE is defined, Method2 if trACE is undefined and DEBUG is defined, and Method3 if both trACE and DEBUG are undefined.

See Also

See the "C# Preprocessor Directives" and "ConditionalAttribute Class" topics in the MSDN documentation.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows