How to Do #Ifdef in C#

how is #if / #endif different than if?

It's useful when you need two (or more) versions of your code with little difference. Then instead of keeping two projects using complier directives like #if TEST you write both versions in the same project. Then from project properties you can set value for TEST llike TEST = true and then compile the project.

#if TEST
Console.WriteLine("Hello World!");
#else
Console.WriteLine("Goodbye World!");
#endif

If TEST = true it's like you just write : Console.WriteLine("Hello World!"); and vice versa.

Issues with using #ifdefs in C#

If you are using Visual Studio is pretty simple to do preprocessor directives.

  1. Select Project
  2. Properties
  3. Build
  4. On 'Conditional compilation symbols' put your wanted symbol and you should have access in all the assembly

If you are not using Visual Studio just follow this topic.

For learning purpose about preprocessor directives you can go to Tutorialspoint C# website.

To write code in C# using C++ approach you can use the following syntax:

#define Name_Value

#if (Name_value)
{
//code
}

If this doesn't fit your needs you can go with a configuration file where you can stack in all your key name - value and access them by ConfigurationManger.AppSettings["Key"]

Another way around this is to have a global enum and assign values.

 public enum DemoEnum
{
Name = Value
}

if(passedEnum == DemoEnum.Name)
{
// code
}

The solution you gave with the constants is not very flexible, because if your code changes you will have to create new constants and the image gets too blurry.

Using readonly properties you can switch values of 'const' in your approach.

public class Global
{
public readonly string Name;

public Global()
{
if(condition)
Name = 10;
else
Name = 30;
}
}

If I forgot an option feel free to comment :)

Parsing #ifdef in C# with Regex

First, the ifdef block needs to be non-greedy, otherwise the fact that the else block is optional would allow it to capture everything up to #endif.

Second, .* will not accept multiple lines by default. You need to enable the single-line option.

This pattern works with your input:

\#ifdef\s+(\S*)[^\n]*
(.*?)
(?:\s*\#else[^\n]*
(.*)
)?\s*\#endif

C# shortcut for #if ... #else ... #endif like #define something as string

Well, you can use a using alias directive:

#if __MonoCS__
using SQLiteConnection = Foo.Bar.SqliteConnection;
#endif

Then you can use SQLiteConnection everywhere within the same file.

You may want to use the adapter pattern to put this just in a single piece of code though, so all the rest of your code can be the same either way.

How do I set a conditional compile variable?

The C# compiler csc.exe and the C# language itself do not expose any predefined constants for conditional compilation. Visual Studio only adds the DEBUG and TRACE values, which can be configured through the IDE. The IDE also lets you add your own arbitrary symbols, but since these are essentially fixed (invariant) values, the capability is of limited use.

More powerful custom options can set up by manually editing your .csproj project file. You can set up conditions here to selectively propagate conditional compilation symbols into C# based on the huge amount of environment and configuration information available in MSBuild (see here and here, but in principle, there can be no complete list, since disparate components arbitrarily contribute metadata ad-hoc).

Let's consider a working example. One case where it's useful to conditionally compile is if you want to write code that adapts to the whatever tools are discovered during the build. This way you can exploit the latest language features while still preserving the ability to compile on machines with older tooling which would, as expected, reject the alien syntax and/or keywords. For the particular case of C# 7.0 in Visual Studio 2017 we can modify the .csproj as follows:

.csproj file (excerpt):

Sample Image

You could also identify each of the older C# compilers as well, degrading gracefully along the way. The same goes for detecting the .NET Framework version (oft-requested on Stack Overflow [1]
[2]
[3]
[4]) and any other ambient build conditions. Such are left as exercises for the reader, but in case you want to copy/paste the highlighted lines from above, here is the text version. As an update over the screenshot, I added single-quotes to the conditional expression here (even though everything seemed to work without them)

<DefineConstants Condition="'$(VisualStudioVersion)'=='15'">CSHARP7</DefineConstants>
<!-- ... -->
<DefineConstants>DEBUG;TRACE;$(DefineConstants)</DefineConstants>
<!-- ... -->
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>

Anyway, in this manner you can now write conditional C# code using #if… #elif… #else… #endif. Continuing the example case, the code below uses new tuple syntax--only available in C# 7--to swap array elements. Incidentally, the tuple version is not only more concise and/or elegant; it also produces excellent CIL code:

#if CSHARP7
(rg[i], rg[j]) = (rg[j], rg[i]); // Swap elements: tuple syntax
#else
var t = rg[i]; // Swap elements: clunky
rg[i] = rg[j];
rg[j] = t;
#endif

Note that the Visual Studio IDE does correctly process your manual .csproj customizations in every regard. Given the .csproj I showed earlier, the IDE code editor properly recognizes and evaluates conditional compilation for the purposes of IntelliSense, refactoring, "dimming-out" inactive blocks of code, etc.

I also mentioned that MSBuild has a treasure trove of information available, of which $(VisualStudioVersion) was just one example. Unfortunately, there's no easy to find out which values are available and what values they might have at buildtime. A trick is to temporarily put a C++ project into your Visual Studio solution (if you don't already have one) alongside your C# project. If you right click the project properties for this .vcxproj and then look at (e.g.) "Additional Include Directories" on the C/C++ page, a dropdown will appear at the far right when you click to edit:

Sample Image

You'll get a dialog box with a "Macros" button which you can click to get a list of all the available MSBuild variables plus their expected values according to platform and configuration that are currently selected in the IDE. Don't overlook the well-known item metadata fields (prefixed with %) at the bottom of the list.

Sample Image

You can get an idea for how much stuff is here from the size of the scrollbar thumb in this screenshot. (They're listed alphabetically; I just scrolled to this part of the 'P' section, because it had minimal personal information.) It's important to note, however, that both the (available) variables and their values evolve over time during the course of the build, so you may find items in this list that aren't available to your .csproj at the time it's processed.


Another way to find out what property values are available during and throughout your build process is to set the MSBuild "output verbosity" to "Detailed", and then rebuild.

Sample Image

After the build finishes, examine the top of the build log in the Visual Studio Output Window, and you'll see a list of the available property names along with their initial values.

Is there an easy way in C# to have conditional compilation symbols based on OS version

I don't think there's a way to conditionally compile code based on OS version. The documentation for #define states (emphasis mine):

Symbols can be used to specify
conditions for compilation. You can
test for the symbol with either #if or #elif.
You can also use the conditional attribute to perform conditional compilation.

You can define a symbol, but you cannot assign a value to a symbol. The #define
directive must appear in the file before you use any instructions
that are not also directives.

You can also define a symbol with the /define compiler option. You can
undefine a symbol with #undef.

A symbol that you define with /define
or with #define does not conflict with
a variable of the same name. That is,
a variable name should not be passed
to a preprocessor directive and a
symbol can only be evaluated by a
preprocessor directive.

The scope of a symbol created by using #define is the file in which it was defined.

You will have to conditionally run it instead:

void TestTxF() {
if (System.Environment.OSVersion.Version.Major < 6) {
// "pass" your test
}
else {
// run it
}
}

Update:

This has been asked before.

How do you use #define?

In C# #define macros, like some of Bernard's examples, are not allowed. The only common use of #define/#ifs in C# is for adding optional debug only code. For example:

        static void Main(string[] args)
{
#if DEBUG
//this only compiles if in DEBUG
Console.WriteLine("DEBUG")
#endif
#if !DEBUG
//this only compiles if not in DEBUG
Console.WriteLine("RELEASE")
#endif
//This always compiles
Console.ReadLine()
}

C# if/then directives for debug vs release

DEBUG/_DEBUG should be defined in VS already.

Remove the #define DEBUG in your code. Set preprocessors in the build configuration for that specific build.

The reason it prints "Mode=Debug" is because of your #define and then skips the elif.

The right way to check is:

#if DEBUG
Console.WriteLine("Mode=Debug");
#else
Console.WriteLine("Mode=Release");
#endif

Don't check for RELEASE.



Related Topics



Leave a reply



Submit