C# !Conditional attribute?

C#Conditional StatementsConditional Compilation

C# Problem Overview


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. ļ•—

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

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.

Solution 3 - C#

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:

  • Create new CSharp class file and copy paste above code;
  • Call from your code the Demonstration.NotCondition.Test(), preferably at the beginning of Program.Main() method;
  • And set or remove "DUMMY" compile condition symbol from project setting to see behavioral difference;

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 ;)

Solution 4 - C#

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);
}

Solution 5 - C#

#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.

Solution 6 - C#

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;

Solution 7 - C#

The NET framework standard library annotated reference doesn't state any. So I'm afraid you'll have to roll your own!

Solution 8 - C#

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 strings 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")].

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIan BoydView Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#SLaksView Answer on Stackoverflow
Solution 3 - C#SoLaRView Answer on Stackoverflow
Solution 4 - C#Riegardt SteynView Answer on Stackoverflow
Solution 5 - C#Kyle WView Answer on Stackoverflow
Solution 6 - C#Ramon SmitsView Answer on Stackoverflow
Solution 7 - C#Roy DictusView Answer on Stackoverflow
Solution 8 - C#v01peView Answer on Stackoverflow