Inspired by @SLaks answer, I came up with the following solution to work around the lack of a negative conditional. Be aware that, like their answer, the evaluation of the [Conditional]
will be depending on the respective preprocessor symbol(s) being present in the file where you define this, rather than the calling one, which is something you may or may not want.
The idea is, that you'll need a preprocessor symbol, that is defined project wide (or that you defined), e.g. NET_STANDARD
. Then you need another preprocessor symbol that is for sure not defined, like THIS_IS_JUST_SOME_RANDOM_STRING_THAT_IS_NEVER_DEFINED
. With these preconditions, you can build the following workaround:
#if ShowDebugString
#undef HideDebugString
#else
#define HideDebugString
#endif
#define SOME_OTHER_CONDITION
public static class Conditional
{
private const string TRUE = "NET_STANDARD"; //pick one that is always be defined in your context
private const string FALSE = "THIS_IS_JUST_SOME_RANDOM_STRING_THAT_IS_NEVER_DEFINED";
#if ShowDebugString
public const string ShowDebugString = TRUE;
#else
public const string ShowDebugString = FALSE;
#endif
#if HideDebugString
public const string HideDebugString = TRUE;
#else
public const string HideDebugString = FALSE;
#endif
#if SOME_OTHER_CONDITION
public const string SOME_OTHER_CONDITION = TRUE;
#else
public const string SOME_OTHER_CONDITION = FALSE;
#endif
}
Now you have some const string
s that you can use for the [Conditional]
attributes, that don't require the caller to have the respective preprocessor symbols defined. This means this approach will also work for any #define
that you make at the start of the code above (which is what I needed). These can then be used for your methods:
[Conditional(Conditional.ShowDebugString)]
public static void ShowDebugString(string s)
{
//...
}
[Conditional(Conditional.HideDebugString)]
public static void ShowReleaseString(string s)
{
//...
}
[Conditional(Conditional.SOME_OTHER_CONDITION)]
public static void SomeOtherMethod()
{
//...
}
While a little tedious to set up, a nice side effect of this approach is, that you can define all the boilerplate code in a separate file once, to not obstruct your main code, or to use it in multiple places. If you just need (or want) that functionality in one spot, you can of course also define all the strings in the same file or class.
Bonus: now it's less easy to screw up, by mistyping the string in the [Conditional("Attribut")]
.
[Conditional(...)]
is not equivalent to that; it removes callsites as well. β Fabre