Click here to Skip to main content
Click here to Skip to main content

Preprocessor Directives in C#

By , 29 Feb 2012
 

Introduction

Preprocessor directives are commands that are interpreted by the compiler and affect the output or behavior of the build process. But the C# compiler does not have a separate preprocessor, like C and C++ you cannot use these directives to create macros. Preprocessing directives are top lines in our program that start with '#'. The '#' is followed by an identifier that is the directive name.

.NET framework 1.1 and above will support these preprocessor directives.

Classification of C# language preprocessor directives are as follows.

Conditional compilation: We can include and exclude parts of the program based on the conditions.

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

Errors , Warnings , Line & pragma: The directive #error initiates the preprocessor to rise error, #warning like #error directive but it prompts warning to the user and continues with the process, #line can be used to hide sections of code from the debugger. The #pragma directive can either suppress or restore specific compiler warnings.

  • #warning
  • #error
  • #line
  • #pragma

Region: If you want to indicate a certain block of source code with a name, you can indicate it with a name and keep the entire block between #region and #endregion. So the C# code file is neatly organized as blocks, and that can be expanded or collapsed visually.

  • #region
  • #endregion

Real Time Usage

When I work with real time environment, the preprocessor directives are very helpful to set conditional compilation like setting up of default parameters based on the defined symbol, and prompting developers in terms of building project, and making conditional warnings and errors, etc.

Sample program to demonstrate it

#define Default
#define DevelopmentMode
#define TestingMode
#undef  Development

#if DEBUG
#warning You should not compile in debug mode, use release mode         
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SampleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            String orgName=String.Empty;
            String email=String.Empty;
            String SourceDb=String.Empty;

            #if(DevelopmentMode)
              {
              SourceDb="C:\\DovDb.Mdb"; //This Db used at the time of development phase
              }
            #else
              {
              SourceDb = "C:\\TestDb.Mdb";//This Db used at the time of testing
              }
            #endif


            #if(Default)
            {
                orgName = "MyOrganization";            
                email = "Default@gmail.com";
                const string logName = @"\myLog.log";  //Write log information
            }
            #else
            {   
                orgName = fetch from database
                email = fetch from database
               const string logName = fetch from database //Write log information
            }
            #endif
        }       
    }
}

Preprocessor directives help the developer to make programming with minimal complexity, improving readability, ease Of maintenance, and prompting invalid changes in the flow of code, etc.

Using the Code

I am going to provide a simple application which will enable you to better understand preprocessor directives in C#, and how we can use it in the real time environment.

In the following, there are 3 private assemblies, viz., version0, version1 and version2. Version0 takes input data from the user. A new feature “AddOperation” is added in Version1 based on the user input values. Version2 has added new feature “SubOperation” and changed the existing “AddOperation” with the default values.

So the users who are going to use “AddOperation” based on the user input values can use version2, but they don’t get the new feature of “SubOperation”.

The following code demonstrates it.

Version0

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PreproDirective
{
    class NumberOperationVersion0
    {
        Int32 x, y;
        public void GetData(Int32 x, Int32 y)
        {
            this.x = x;
            this.y = y;

        }
        public Int32 AddOperation()
        {
            //Not implemented
            return 0;
        }

        public Int32 SubOperation()
        {
            //Not implemented
            return 0;
        }
    }
}

The above version0 has the feature to take data from the user but does not have any operations.

Version1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PreproDirective
{
    public class NumberOperationVersion1
    {
        Int32 x, y;
        public void GetData(Int32 x,Int32 y)
        {
            this.x = x;
            this.y = y;
        }
        public Int32 AddOperation() 
        {
    //Operation performed based on User input values 
            return x + y;
        }
        public Int32 SubOperation()
        {
            //Not implemented in this version1
            return 0;
        }
    }
}

This version1 adds "AddOperation" feature by taking user input.

Version2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PreproDirective
{
    public class NumberOperationVersion2
    {
        Int32 x, y;
        public void GetData(Int32 x,Int32 y)
        {
            this.x = x;
            this.y = y;
        }

        public Int32 AddOperation(Int32 x,Int32 y)
        {
//Operation performed based on default values passed from main program
            return x + y;
        }

        public Int32 SubOperation()
        {
            return x-y;
        }           
    }
}

This version2 implemented the feature "SubOperation", and changed "AddOperation" by taking default values.

Main Program

#define version0   
#define version1
#define version2
#undef  version0      // if you comment it,  error raised

#if DEBUG
#warning Compiled in DEBUG mode.      // use release mode to prevent this warning
#endif

#if version0
#error Version0 is not working.    //Error will be raised when version0 is defined
#endif

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PreproDirective
{
    class Program
    {
        static void Main(string[] args)
        {
            Int32 x=0,y=0;

                #pragma warning disable
                       #line 20 "NumberOperationVersion1.cs"
                       #warning "Changes in Earlier versions are not allowed"
                       #line default
                #pragma warning restore

                #line 20 "NumberOperationVersion0.cs"
                #warning "Changes in version0 are not allowed"
                #line default

            //Version 1 changes
            #region V1
                    #if(version1)
                    {
                       NumberOperationVersion1 objVer1=new NumberOperationVersion1();
                       Console.WriteLine("Enter Xvalue,Yvalue to check version1
                       compatibility with ADD and Sub Operation");
                        
                        Console.WriteLine("Enter Xvalue:");
                        x=Convert.ToInt32(Console.ReadLine());

                        Console.WriteLine("Enter Yvalue:");
                        y = Convert.ToInt32(Console.ReadLine());
                        objVer1.GetData(x,y);
                    Console.WriteLine("AddOperation Result is:"+objVer1.AddOperation());
              Console.WriteLine("SubOperation is not working with this version \n\n\n\n");
                    }
                    #endif
            #endregion V1

             //Version 2 changes
            #region V2                              

                     #if (version2)
                       {
                         NumberOperationVersion2 objVer2=new NumberOperationVersion2();
                         Console.WriteLine("Enter Xvalue,Yvalue to check version2
                         compatibility with ADD and Sub Operation");
                         Console.WriteLine("Enter Xvalue:");
                         x = Convert.ToInt32(Console.ReadLine());

                         Console.WriteLine("Enter Yvalue:");
                         y = Convert.ToInt32(Console.ReadLine());
                         objVer2.GetData(x,y);
                        Console.WriteLine("AddOperation Result is(Based on the default
                        values version-2 is):"+objVer2.AddOperation(10,12));
                        Console.WriteLine("SubOperation Result is (Based on user input):"+
                        objVer2.SubOperation());
                    }
                    #else
                    {
                      Console.WriteLine("No Version found");
                    }
                    #endif

            #endregion V2                     
            Console.ReadKey();
        }
    }
}

On the top of the program, versions are defined, and the unused version0 is undefined.

#if DEBUG
#warning Compiled in DEBUG mode.
#endif 

If we run the application in debug mode, it will throw a warning as shown in Fig.1.

#if version0
#error Version0 is not working.    
#endif

If version0 is defined but unused, so it should be reversed to undefined; lest it will throw a fatal error as "Version0 is not working" shown in the Fig.1:

Fig-1
#pragma warning disable
#line 20 "NumberOperationVersion1.cs"
#warning "Changes in Earlier versions are not allowed"
#line default
#pragma warning restore 

In the main program, the above statement throws a warning at line number 20 in "NumberOperationVersion1.cs" class. But the statement “#pragma warning disable” will disable the warning.

#line 20 "NumberOperationVersion0.cs"
#warning "Changes in version0 are not allowed"
#line default

Statement throws warning at line number 20 in "NumberOperationVersion0.cs" class:

Fig-2

The regions can be understood from the following figure:

Fig-3

The above program is taken to easily illustrate the working of preprocessor directives.

Conclusion

This article gives you a better understanding of preprocessor directives in C#. Hope to get your feedback and suggestions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sridhar Patnayak
Software Developer (Senior) RMIT Solutions
India India
Member
Experienced IT professional in VB,C#,ASP.net,MVC,Remoting and WCF.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 4memberjohannesnestler2 May '13 - 2:58 
good, examples could be better - see my other comments
GeneralRe: My vote of 4memberSridhar Patnayak5 May '13 - 22:49 
valuable comments, Thanks johannesnestler Thumbs Up | :thumbsup:
SuggestionI don't think this is the right way of writing c-sharp codememberls2514513 Sep '12 - 22:04 
It's hard to understand.
We have more elegant way to do this by using 'Conditional'.
GeneralRe: I don't think this is the right way of writing c-sharp codememberjohannesnestler2 May '13 - 2:48 
I strongly disagree - conditional code(-branching) is not comparable to conditional compilation! Sorry for saying it that way, I think you have no clue about cc - please see my comments to wb's message: Preprocessor Directives in C#[^]
GeneralMy vote of 1memberwb12 Mar '12 - 23:14 
the concept of conditional compilation as described in this article ist just wrong. The settings in the first example must go to a config file and not compiled.
the choise of the class to load must be encapsulated into proper assemblies and loaded at runtime for example.
 
the code in the examples is very very hard to read and understand.
GeneralRe: My vote of 1memberjohannesnestler2 May '13 - 2:44 
You may make some valid points about how you would make different versions of your software by configuration. But I think what you are missing is that conditional compilation has a big impact on the resulting assembly. You can: exclude code from going into the assembly that will never be needed. Think about making just a simple control for different frameworks/runtime environments (e.g. have you ever created a UserControl for Windows CE? - you will have to write a second for design time), or a function interacting with different OSes (e.g. Windows/Linux (Mono)). You don't want your Windows code going in to your Linux assembly. This is the important point: With Conditional compilation you can hide code from the Compiler - while with normal conditions the code will still be there, just unused. After saying so I'd also suggest you have a look into the possibillity to create different solution configuration within VS (I think you know the default ones DEBUG and RELEASE), define different symbols there, and in connection with conditional compilation you can create different "flavours" of your project.
 
But I have to agree with you - examples are maybe not best in this article and conditional code is always difficult to read and kind of ugly. Downvoting to 1 is a bit hard in my opinion, cause from your reasoning I have to assume you didn't use CC for yourself. - give it a try! Rose | [Rose]
GeneralRe: My vote of 1memberwb12 May '13 - 23:42 
I still do not agree with you. You seem to understand this concept pretty well so you try to advocate it. I have a quite a C/C++ Backround and hat to use the preprocessor a lot in the past. Your article is for newbies and if they look at your code and examples they may think it is a good way to solve such simple problems you show. And the fact is, it is not! Sometimes you need the conditional compilation but in .NET it is realy not that important than it is in C/C++.
If I need different functionality, I create separated modules and than configure the Application to use the appropriate module. From your examples I guess that you are using the right tool for the wrong problem. Smile | :)
GeneralRe: My vote of 1memberjohannesnestler13 May '13 - 4:50 
Hi wb.
 
I think you overlooked that I'm not the author of this article. And I think - like you - samples are not the Best. There are still valid reasons to use compiler symbols. I come from C++ tooconfiguration, so I know how to abuse preprocessor directives, especially macros 😉. And if you talk about app configuration I'm with you. But you can never configure different compilation or assembling with any runtime code. Most important if you write cross-platform Code.
GeneralNice !memberraananv10 Mar '12 - 8:18 
10x for share ! Laugh | :laugh:
GeneralMy vote of 5memberAnurag Gandhi29 Feb '12 - 22:11 
Nice piece of information.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 29 Feb 2012
Article Copyright 2011 by Sridhar Patnayak
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid