In the last couple of years, programming in .NET (.NET Core,
Xamarin, MAUI) is multi-targeting different frameworks or
platforms (NETCOREAPP, NET4, Windows, Android, iOS, . . .) and
conditional compilation is used more than ever. By inertia, many
of us who switched from C++ to C# continue to use preprocessor
directives to control conditional compilation(#if - #endif
).
.NET is offering another approach that is in some cases a cleaner
option.
Let’s define symbol LOAD_TEST
to load more data for
testing purposes. In this dummy example symbol is simply defined
at the top of the file, but in the real life symbol would be
defined in command-line options or environment variable. I
intentionally didn’t want to use well known DEBUG
symbol.
When symbol LOAD_TEST
is defined, both functions will
expand Pi with more digits. If symbol is not defined, the two
approaches are similar but not quite the same.
Let’s observe ILSpy output below without LOAD_TEST
defined.
Call to LoadTest1()
is also compiled out even code itself
is not polluted with #if - #endif
like call to LoadTest2()
.
Using conditional attributes is sometimes a cleaner solution.
Attribute can be applied to classes too and all usages of this
class will be compiled out.
But there is a downside. Method guarded with conditional
attribute must return void
. Another downside of
conditional attributes is that LoadTest1()
is still present
and compiled (only call is eliminated). If you are switching
between frameworks like iOS/Android/Win in Maui app, method code
must compile on all platforms and that is usually not the case. In
this case better option is to use #if - #elif - #endif
and
make 3 different versions of the same method.