Reputation: 256581
Does C# have a not Conditional
(!Conditional
, NotConditional
, Conditional(!)
) attribute?
i know C# has a Conditional
attribute:
[Conditional("ShowDebugString")]
public static void ShowDebugString(string s)
{
...
}
which is equivalent1 to:
public static void ShowDebugString(string s)
{
#if ShowDebugString
...
#endif
}
But in this case i want the inverse behavior (you have to specifically opt out):
public static void ShowDebugString(string s)
{
#if !RemoveSDS
...
#endif
}
Which leads me to try:
[!Conditional("RemoveSDS")]
public static void ShowDebugString(string s)
{
...
}
which doesn't compile. And:
[Conditional("!RemoveSDS")]
public static void ShowDebugString(string s)
{
...
}
which doesn't compile. And:
[NotConditional("RemoveSDS")]
public static void ShowDebugString(string s)
{
...
}
which doesn't compile because it's only wishful thinking.
1 Not true, but true enough. Don't make me bring back the Nitpicker's Corner. 🕗
Upvotes: 77
Views: 35348
Reputation: 886
True we can't 'NOT' ConditionalAttribute, but we can 'NOT' the condition as presented below.
// at the beginning of the code before any using clauses
// we shall negate the condition.
#if DUMMY
#undef NOT_DUMMY
#else
#define NOT_DUMMY
#endif
using System;
using System.Diagnostics; // required by ConditionalAttribute
namespace Demonstration
{
public static class NotCondition
{
/// <summary>
/// The following method is called when 'DUMMY' is defined in project settings
/// </summary>
[Conditional("DUMMY")]
static void ShowDebugStringDUMMY(string s)
{
Debug.WriteLine($"called {nameof(ShowDebugStringDUMMY)}({s})");
}
/// <summary>
/// The following method is called when 'DUMMY' is not defined in project settings
/// </summary>
[Conditional("NOT_DUMMY")]
static void ShowDebugStringNOTDUMMY(string s)
{
Debug.WriteLine($"called {nameof(ShowDebugStringNOTDUMMY)}({s})");
}
/// <summary>
/// Example that calls to methods that are included in context of conditional method as arguments shall not be executed.
/// </summary>
static string GetText(string s)
{
Debug.WriteLine($"{nameof(GetText)}({s})");
return s;
}
public static void Test()
{
// following method is compiled
ShowDebugStringDUMMY(GetText("dummy"));
ShowDebugStringNOTDUMMY(GetText("!dummy"));
}
}
}
Now compile of specific methods depends on definition of Conditional Compilation Symbols for the project that is including this file.
Project Properties -> Build -> Conditional compilation symbols
Conditional compilation symbols
Specifies symbols on which to perform conditional compilation. Separate symbols with a semi-colon (";"). For more information, see /define (C# Compiler Options).
How to use demonstration code:
If we HAVE NOT set compile condition symbol "DUMMY" in project settings, when project run the Output Window shall display the following:
GetText(!dummy)
called ShowDebugStringNOTDUMMY(!dummy)
Otherwise, if we HAVE set compile condition symbol "DUMMY" in project settings, when project run the Output Window shall display the following:
GetText(dummy)
called ShowDebugStringDUMMY(dummy)
- NOTE: the project conditional compile symbols are only visible by files inside that project, and not by files in some referenced project files.
hope this helps you solve your problem ;)
Upvotes: 18
Reputation: 1136
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")]
.
Upvotes: 1
Reputation: 2618
I liked the approach @Heliac mentioned and made a helper class for it:
class Build
{
public static bool IsDebug { get; private set; }
static Build()
{
CheckDebugMode();
}
[System.Diagnostics.Conditional("DEBUG")]
static void CheckDebugMode()
{
IsDebug = true;
}
}
Although the usefulness for DEBUG
isn't immediately
This allows you to do things like
static readonly bool UseCaching = !Build.IsDebug;
Upvotes: 1
Reputation: 1499770
First, having the Conditional
attribute is not equivalent to having #if
inside the method. Consider:
ShowDebugString(MethodThatTakesAges());
With the real behaviour of ConditionalAttribute
, MethodThatTakesAges
doesn't get called - the entire call including argument evaluation is removed from the compiler.
Of course the other point is that it depends on the compile-time preprocessor symbols at the compile time of the caller, not of the method :)
But no, I don't believe there's anything which does what you want here. I've just checked the C# spec section which deals with conditional methods and conditional attribute classes, and there's nothing in there suggesting there's any such mechanism.
Upvotes: 62
Reputation: 5701
Just adding my 2 cents, three years down the line :-) ... I use a [Conditional("DEBUG")]
method to set an IsDebugMode
property to check the reverse. Hacky, but it works:
private bool _isDebugMode = false;
public bool IsDebugMode
{
get
{
CheckDebugMode();
return _isDebugMode;
}
}
[Conditional("DEBUG")]
private void CheckDebugMode()
{
_isDebugMode = true;
}
private void DisplaySplashScreen()
{
if (IsDebugMode) return;
var splashScreenViewModel = new SplashScreenVM(500)
{
Header = "MyCompany Deals",
Title = "Main Menu Test",
LoadingMessage = "Creating Repositories...",
VersionString = string.Format("v{0}.{1}.{2}",
GlobalInfo.Version_Major, GlobalInfo.Version_Minor, GlobalInfo.Version_Build)
};
SplashScreenFactory.CreateSplashScreen(splashScreenViewModel);
}
Upvotes: 18
Reputation: 887195
Nope.
Instead, you can write
#if !ShowDebugString
[Conditional("FALSE")]
#endif
Note that unlike [Conditional]
, this will be determined by the presence of the symbol in your assembly, not in your caller's assembly.
Upvotes: 60
Reputation: 3752
#ifndef ShowDebugString
#define RemoveSDS
#endif
?
edit: For more clarification. If ShowDebugString is defined Conditional["ShowDebugString"]
will be called. If ShowDebugString is not defined, Conditional["RemoveSDS"]
will be called.
Upvotes: 5
Reputation: 33139
The NET framework standard library annotated reference doesn't state any. So I'm afraid you'll have to roll your own!
Upvotes: 0