Skip to main content

Conditional compilation symbols in C#

A few months ago, I created a scalable data migration tool for a customer using Dataflow. I’ll promise I write a blog post about it when time permits.

The tool was originally created with some assumptions about your primary key strategy; in this case that an identity column was used. However this week I was asked if the tool could be used for another application. The only problem that for this application a sequential guid was used for primary key values.

I could make the full application configurable to handle multiple key strategies but as this was a one shot migration, I decided to use a different strategy and use conditional compilation symbols to handle this scenario.

Let me first explain what conditional compilation symbols are…

What Are Conditional Compilation Symbols?

Conditional compilation symbols in C# are essentially preprocessor directives that allow the compiler to include or omit portions of code based on certain conditions. These symbols are particularly useful when you need to control how different builds behave without changing the underlying codebase.

The common use cases include:

  • Debug vs Release builds
  • Platform-specific code (e.g., Windows vs macOS vs Linux)
  • Experimental features or beta testing
  • Configuration management (e.g., enabling/disabling logging)

You use these compilation symbols in combination with the following directives in C#:

  • #define and #undef: These allow you to define or undefine symbols within a file.
  • #if, #elif, #else, and #endif: These are used to conditionally compile code based on whether a symbol is defined or not.

Let’s take a quick look at a basic example:

Here, when the DEBUG symbol is defined, the first Console.WriteLine statement will be included in the build. If it’s not defined, the second statement for release mode will be included instead.

Defining Conditional Compilation Symbols

You can define symbols in several places, making them flexible for various scenarios:

  • In Code: As seen in the example above, you can define them directly in the code file using #define:
  • Project Properties: This can be done directly in the csproj file:

or through the Visual Studio UI: Right click on your project -> Properties –> Build –> General:


  • Command Line: When compiling with dotnet build or csc, you can pass symbols as command-line arguments using –p:DefineConstants:

dotnet build -p:DefineConstants="PLATTELANDSLOKET"

Each method offers flexibility depending on where and how you need to apply these symbols.

As I mentioned in the introduction I used the approach above to conditionally switch between the 2 primary key strategies. So you'll find multiple #if#else,#endif directives spread out through the codebase:

Some heuristics when using conditional compilation symbols

While conditional compilation is a powerful feature, it can also lead to code that is harder to maintain if overused. Here are some best practices to follow:

  • Minimize Use: Only use conditional compilation where absolutely necessary. It can complicate code readability and maintainability. The example I shared was a good use case especially as the impact was limited. 
  • Keep Code Modular: Use conditional compilation at a high level (e.g., method or class) rather than for individual statements or lines. This keeps the logic clean and manageable.
  • Document it: Ensure that your conditional symbols are well-documented, either in your code comments or your project's documentation, so team members know why certain code paths are being excluded or included. It introduces another level of magic so make sure that people are aware that these symbols are available and/or in use.

Happy coding!

More information

Preprocessor directives - C# reference | Microsoft Learn

Dataflow (Task Parallel Library) - .NET | Microsoft Learn

SQL Server identity column - Simple Talk (red-gate.com)

Popular posts from this blog

Azure DevOps/ GitHub emoji

I’m really bad at remembering emoji’s. So here is cheat sheet with all emoji’s that can be used in tools that support the github emoji markdown markup: All credits go to rcaviers who created this list.

DevToys–A swiss army knife for developers

As a developer there are a lot of small tasks you need to do as part of your coding, debugging and testing activities.  DevToys is an offline windows app that tries to help you with these tasks. Instead of using different websites you get a fully offline experience offering help for a large list of tasks. Many tools are available. Here is the current list: Converters JSON <> YAML Timestamp Number Base Cron Parser Encoders / Decoders HTML URL Base64 Text & Image GZip JWT Decoder Formatters JSON SQL XML Generators Hash (MD5, SHA1, SHA256, SHA512) UUID 1 and 4 Lorem Ipsum Checksum Text Escape / Unescape Inspector & Case Converter Regex Tester Text Comparer XML Validator Markdown Preview Graphic Color B

Kubernetes–Limit your environmental impact

Reducing the carbon footprint and CO2 emission of our (cloud) workloads, is a responsibility of all of us. If you are running a Kubernetes cluster, have a look at Kube-Green . kube-green is a simple Kubernetes operator that automatically shuts down (some of) your pods when you don't need them. A single pod produces about 11 Kg CO2eq per year( here the calculation). Reason enough to give it a try! Installing kube-green in your cluster The easiest way to install the operator in your cluster is through kubectl. We first need to install a cert-manager: kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.yaml Remark: Wait a minute before you continue as it can take some time before the cert-manager is up & running inside your cluster. Now we can install the kube-green operator: kubectl apply -f https://github.com/kube-green/kube-green/releases/latest/download/kube-green.yaml Now in the namespace where we want t