#507 – You can #define Other Symbols within a #if Block

Within the body of an #if/#endif pair, you can include #define or #undef directives.  If the body of the #if does contain #define or #undef directives, it must appear before the first token of the containing source file.

#define DOGSBARK

#if DOGSBARK || DOGSGROWL
#define DOGSMAKENOISE
#endif

using System;
using DogLibrary;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            Dog d = new Dog("Kirby", 12);
#if DOGSMAKENOISE
            d.BarkOrGrowl();
#endif
        }
    }
}

#503 – Conditionally Compile Code Using #if / #endif Directives

Once you define a conditional compilation symbol using the #define directive, you can use the #if directive to conditionally compile code when the corresponding conditional compilation symbol is defined.

In the code sample below, the Dog.Bark method is called, because the DOGSBARK symbol is defined and so the line containing the call to Bark is compiled.

#define DOGSBARK

using System;
using DogLibrary;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            Dog d1 = new Dog("Kirby", 12);
#if DOGSBARK
            d1.Bark();
#endif
        }
    }
}

If we don’t define the DOGSBARK symbol, then the compiler doesn’t compile the line between the #if and #endif directives.  In fact, since the compiler doesn’t even look at these lines in this case, they don’t even have to contain valid C# syntax.

#502 – #define and #undef Scope

When you include a #define or #undef preprocessor directive in a file, the scope in which that conditional symbol is defined (or not defined) is limited to that single file.

For example, suppose that we define the symbol QUIET within a file that creates an instance of a Dog and then calls the Bark method of the Dog object.

// Code from Program.cs
#define QUIET

using System;
using DogLibrary;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            Dog d1 = new Dog("Kirby", 12);
            d1.Bark();
        }
    }
}

Let’s also suppose that the Dog.Bark method, which exists in a separate file, compiles differently depending on whether the symbol QUIET is defined.

// Code from Dog.cs
        public void Bark()
        {
#if QUIET
            Console.WriteLine("Arf");
#else
            Console.WriteLine("WOOOOOF!");
#endif
        }

Because QUIET is defined in Program.cs but not in Dog.cs, the Bark method will use the second (not quiet) line.

#501 – Differences Between #define in C++ and C#

In C++, you could use the #define preprocessor directive in three ways:

  • Define a symbol and then later check to see if the symbol exists
  • Define a symbol, giving it a particular value that you can later check
  • Define a symbol and give it a definition (a macro)
  • Provide parameters as part of the symbol’s definition

In C++, the #define directive assigns some arbitrary text to a symbol and the preprocessor substitutes the text for the symbol, wherever the symbol appears in your code.

As an example, you could use C++ to create a macro by defining the symbol MAX as follows:

#define MAX(a,b) (a>b)?a:b

The #define directive in C# is much more limited.  Of the four uses for #define listed above, only the first one is supported in C#.  You use the #define directive to define a symbol that is either present or not present.

#500 – #define and #undef Must Be at Top of File

In C#, all #define and #undef  preprocessor directives must appear at the top of a source code file, i.e. before any valid C# tokens.  The definition (or un-definition) then remains in effect until the end of the file is reached.

#define QUIET

using System;
using DogLibrary;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            Dog d1 = new Dog("Kirby", 12);
#if QUIET
            d1.Bark("Arf");
#else
            d1.bark("WOOOF");
#endif
        }
    }
}

In this case, because the conditional compilation symbol is included directly in the code, Intellisense already knows which of the two Bark methods will be included in the code.  It displays the line that will not be compiled in grey.

In this example, the second Bark method is not compiled at all.

 

#499 – Conditional Compilation Symbols Are Defined or Undefined

At any given point in your source code, while the compiler is compiling that section of code, a particular conditional compilation symbol is either defined or undefined.

You define a conditional compilation symbol

  • Using the #define preprocessor directive
  • Using the /define option on the compiler command line
  • Implicitly (for DEBUG and TRACE symbols), when selecting a build option for a project

You undefine a conditional compilation symbol

  • Using the #undef preprocessor directive
  • When reaching the end of a source file

A conditional compilation symbol is either defined or undefined–it has no value.

You check whether a conditional compilation symbol is defined by using the #if or #elif preprocessor directives.

A conditional compilation symbol remains defined in a source file until the end of the containing source file or until encountering an #undef directive (whichever comes first).

#480 – Pre-Processing Directives

Pre-processing directives are elements that you can include in your source code that are not part of the source code itself.  Instead, they instruct the C# compiler to perform some particular action, like skipping compilation of a section of the code.

The pre-processing directives available in C# include:

  • #define/#undef – Define/undefine condition compilation symbols (see #if)
  • #if/#elif/#else/#endif – Skip compilation of a section of code based on the existence of a conditional compilation symbol
  • #line – specifies a line number to use when reporting errors
  • #error – issue errors during compilation
  • #warning – issue warnings during compilation
  • #region/#endregion – define a collapsible code region, to improve readability
  • #pragma warning – disable or restore certain compiler warnings
  • #pragma checksum – generate a checksum for an ASP.NET page, for debugging purposes