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.
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"; }
#else
{
SourceDb = "C:\\TestDb.Mdb"; }
#endif
#if(Default)
{
orgName = "MyOrganization";
email = "Default@gmail.com";
const string logName = @"\myLog.log"; }
#else
{
orgName = fetch from database
email = fetch from database
const string logName = fetch from database }
#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()
{
return 0;
}
public Int32 SubOperation()
{
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()
{
return x + y;
}
public Int32 SubOperation()
{
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)
{
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 DEBUG
#warning Compiled in DEBUG mode. #endif
#if version0
#error Version0 is not working. #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
#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
#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.