Did you know that C# actually does have a preprocessor? Probably the
most common C# preprocessor directive that you see is the
#region
directive, and that doesn't even have any impact on the code. Now the
preprocessor for C# is nowhere near as powerful as the ones for C and
C++ (for instance, no macros), but it does let you do a couple of handy
things.
C# actually has almost all the standard preprocessor directives - it
just happens to be that the functionality of some of them (specifically
the
#define
directive) is quite reduced. The one notable directive
that is missing is #include
- and it makes sense that C# wouldn't
have it, because C# gets the same sort of functionality from the
using
statements (although there is the fact that #include
refers to
files and using
refers to assemblies - so they are definitely not
equivalent).
So let's start at the beginning:
#define
and #undef
. The directive
#define
gives you the ability to define a symbol, and #undef
lets
you un-define it. For instance:#define MY_SYMBOL
/* Do Stuff */
#undef MY_SYMBOL
However, while you can define a symbol, you cannot assign a value to it
(which is where that major difference from C/C++ comes into play):
#define MY_SYMBOL 42
//Error: Single-line comment or end-of-line expected
So what good is defining a symbol when you can't actually give it a
value? Because in C/C++, giving it a value was really the whole point.
Well, now that you can't give it a value, the only place to use them are
in the
#if
and #elif
directives:#define SYMBOL_A
#define SYMBOL_B
using System;
public class Foo
{
static void Main()
{
#if (SYMBOL_A && !SYMBOL_B)
Console.WriteLine("SYMBOL_A!!");
#elif (!SYMBOL_A && SYMBOL_B)
Console.WriteLine("SYMBOL_B!!");
#elif (SYMBOL_A && SYMBOL_B)
Console.WriteLine("SYMBOL_A and SYMBOL_B!!");
#else
Console.WriteLine("Neither!!");
#endif
}
}
That code sample pretty much covers all the craziness that you can do
with the conditional directives. This particular code block would end up
printing out
"SYMBOL_A and SYMBOL_B!!"
, because both symbols were
defined. If, say, I had thrown an #undef
in there, we might get
something else:#define SYMBOL_A
#define SYMBOL_B
#undef SYMBOL_A
using System;
public class Foo
{
static void Main()
{
#if (SYMBOL_A && !SYMBOL_B)
Console.WriteLine("SYMBOL_A!!");
#elif (!SYMBOL_A && SYMBOL_B)
Console.WriteLine("SYMBOL_B!!");
#elif (SYMBOL_A && SYMBOL_B)
Console.WriteLine("SYMBOL_A and SYMBOL_B!!");
#else
Console.WriteLine("Neither!!");
#endif
}
}
Now, this code block would end up printing
"SYMBOL_B!!"
.
But now on to my favorite two directives:
#error
and #warning
. They
essentially allow you to inject compile errors and warnings into the
code. For example:using System;
public class Foo
{
static void Main()
{
#error My Best Error Ever!!
#warning A Little Tiny Warning
}
}
//Error: #error: 'My Best Error Ever!!'
//Warning: #warning: 'A Little Tiny Warning'
Simple, and yet quite effective. I use them as almost a to-do list -
whenever I'm leaving a section of code that I know is wrong, I'll leave
some
#error
or #warning
directives so that the compiler will remind
me to come back later and fix it.
Next up:
#pragma
. The #pragma
directive is kind of a catch-all in
C/C++, and it is the same here in C#. But there is one #pragma
varient that is probably useful to know: #pragma warning
.using System;
public class Foo
{
static void Main()
{
int foo;
Console.WriteLine("Hi");
}
}
//Warning: The variable 'foo' is declared but never used
Generally, the code above would throw the warning that you see there.
But say you want to ignore that warning - you can use the
#pragma warning
directive to get rid of it:using System;
public class Foo
{
static void Main()
{
#pragma warning disable 0168
int foo;
#pragma warning restore 0168
Console.WriteLine("Hi");
}
}
Essentially, what that does is disable the warning number CS0168 between
the
disable
and restore
directives. You should be careful when using
this directive, because if you don't restore the warning, it will be
disabled for the rest of the file. This #pragma warning
directive is
actually quite a bit more powerful than displayed here, and if you'd
like to learn more, you should read about it at
MSDN.
Well, that concludes a nice overview of C#'s preprocessor directives.
We did not cover everything, because some things (like
#line
) could
probably get a whole tutorial on their own. Also, if you would like to
know more about what can be done with the #pragma
directive, you can
check out this MSDNpage (there is a
lot of stuff).
Comments
Post a Comment