#if DEBUG vs. Conditional("DEBUG")

317,436

Solution 1

It really depends on what you're going for:

  • #if DEBUG: The code in here won't even reach the IL on release.
  • [Conditional("DEBUG")]: This code will reach the IL, however calls to the method will be omitted unless DEBUG is set when the caller is compiled.

Personally I use both depending on the situation:

Conditional("DEBUG") Example: I use this so that I don't have to go back and edit my code later during release, but during debugging I want to be sure I didn't make any typos. This function checks that I type a property name correctly when trying to use it in my INotifyPropertyChanged stuff.

[Conditional("DEBUG")]
[DebuggerStepThrough]
protected void VerifyPropertyName(String propertyName)
{
    if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        Debug.Fail(String.Format("Invalid property name. Type: {0}, Name: {1}",
            GetType(), propertyName));
}

You really don't want to create a function using #if DEBUG unless you are willing to wrap every call to that function with the same #if DEBUG:

#if DEBUG
    public void DoSomething() { }
#endif

    public void Foo()
    {
#if DEBUG
        DoSomething(); //This works, but looks FUGLY
#endif
    }

versus:

[Conditional("DEBUG")]
public void DoSomething() { }

public void Foo()
{
    DoSomething(); //Code compiles and is cleaner, DoSomething always
                   //exists, however this is only called during DEBUG.
}

#if DEBUG example: I use this when trying to setup different bindings for WCF communication.

#if DEBUG
        public const String ENDPOINT = "Localhost";
#else
        public const String ENDPOINT = "BasicHttpBinding";
#endif

In the first example, the code all exists, but is just ignored unless DEBUG is on. In the second example, the const ENDPOINT is set to "Localhost" or "BasicHttpBinding" depending on if DEBUG is set or not.


Update: I am updating this answer to clarify an important and tricky point. If you choose to use the ConditionalAttribute, keep in mind that calls are omitted during compilation, and not runtime. That is:

MyLibrary.dll

[Conditional("DEBUG")]
public void A()
{
    Console.WriteLine("A");
    B();
}

[Conditional("DEBUG")]
public void B()
{
    Console.WriteLine("B");
}

When the library is compiled against release mode (i.e. no DEBUG symbol), it will forever have the call to B() from within A() omitted, even if a call to A() is included because DEBUG is defined in the calling assembly.

Solution 2

Well, it's worth noting that they don't mean the same thing at all.

If the DEBUG symbol isn't defined, then in the first case the SetPrivateValue itself won't be called... whereas in the second case it will exist, but any callers who are compiled without the DEBUG symbol will have those calls omitted.

If the code and all its callers are in the same assembly this difference is less important - but it means that in the first case you also need to have #if DEBUG around the calling code as well.

Personally I'd recommend the second approach - but you do need to keep the difference between them clear in your head.

Solution 3

I'm sure plenty will disagree with me, but having spent time as a build guy constantly hearing "But it works on my machine!", I take the standpoint that you should pretty much never use either. If you really need something for testing and debugging, figure out a way to make that testability seperate from the actual production code.

Abstract the scenarios with mocking in unit tests, make one off versions of things for one off scenarios you want to test, but don't put tests for debug into the code for binaries which you test and write for production release. These debug tests just hide possible bugs from devs so they aren't found until later in the process.

Solution 4

This one can be useful as well:

if (Debugger.IsAttached)
{
...
}

Solution 5

With the first example, SetPrivateValue won't exist in the build if DEBUG is not defined, with the second example, calls to SetPrivateValue won't exist in the build if DEBUG is not defined.

With the first example, you'll have to wrap any calls to SetPrivateValue with #if DEBUG as well.

With the second example, the calls to SetPrivateValue will be omitted, but be aware that SetPrivateValue itself will still be compiled. This is useful if you're building a library, so an application referencing your library can still use your function (if the condition is met).

If you want to omit the calls and save the space of the callee, you could use a combination of the two techniques:

[System.Diagnostics.Conditional("DEBUG")]
public void SetPrivateValue(int value){
    #if DEBUG
    // method body here
    #endif
}
Share:
317,436
Lucas B
Author by

Lucas B

Health first. Software second. Coffee and cupcakes. My CV

Updated on November 23, 2020

Comments

  • Lucas B
    Lucas B over 3 years

    Which is better to use, and why, on a large project:

    #if DEBUG
        public void SetPrivateValue(int value)
        { ... }
    #endif
    

    or

    [System.Diagnostics.Conditional("DEBUG")]
    public void SetPrivateValue(int value)
    { ... }
    
  • Lucas B
    Lucas B over 13 years
    +1 for calling code will need to have #if statements as well. Which means there will be a proliferation of #if statements...
  • Richard Ev
    Richard Ev over 13 years
    I totally agree with you Jimmy. If you're using DI and mocking for your tests, why would you need #if debug or any similar construct in your code?
  • Apeiron
    Apeiron over 12 years
    The #if Debug for DoSomething doesn't need to have all the calling statements surrounded by #if DEBUG. you can either 1: just #if DEBUG the inside of DoSomething, or, do a #else with a blank definition of DoSomething. Still your comment helped my understand the difference, but #if DEBUG need not be as ugly as you've demonstrated.
  • Jeff Yates
    Jeff Yates over 10 years
    If you just #if DEBUG the contents, the JIT may still include a call to the function when your code runs in a non-debug build. Using the Conditional attribute means the JIT knows not to even output the callsite when in a non-DEBUG build.
  • Jeff Yates
    Jeff Yates over 10 years
    Conditional doesn't exclude the code. It marks it with metadata. The DEBUG value is matched up at compile time of the CALLING code. This may be inside the assembly that has the marked method or outside of it.
  • myermian
    myermian over 10 years
    @JeffYates: I don't see how what you are writing is any different than what I explained.
  • Gone Coding
    Gone Coding over 10 years
    Rather than just for testing, we often do things like setting a default recipient email to ourselves, in debug builds, using #if DEBUG so that we do not accidentally spam others while testing a system that must transmit emails as part of the process. Sometimes these are the right tools for the job :)
  • MikeT
    MikeT over 9 years
    @Apeiron if you only have the function content in the #if debug then the function call is still added to the call stack, while this is usually not very important, adding the declaration and the function call to the #if means the compiler behaves as if function doesn't exist, so m-y's method is the more "correct" way of using #if. though both methods produce results that are indistinguishable from each other in normal use
  • MikeT
    MikeT over 9 years
    I would generally agree with you but if you are in a situation where performance is paramount then you don't want to clutter the code with extraneous logging and user output, but i do 100% agree that they shouldn't ever be used to alter the fundamental behavior
  • lysergic-acid
    lysergic-acid over 9 years
    While the second option (Conditional attribute) is nicer and cleaner in some cases, it may be needed to communicate the fact that a method call would be stripped from the assembly during compilation (by a naming convention, for example).
  • Ted Bigham
    Ted Bigham about 7 years
    -1 There's nothing wrong with using either of these. Claiming unit tests and DI somehow replaces a debug enabled build of a product is naive.
  • jbyrd
    jbyrd almost 7 years
    if anyone's wondering, IL = Intermediate Language - en.wikipedia.org/wiki/Common_Intermediate_Language
  • Groo
    Groo over 5 years
    It's worth mentioning that parameter evaluation is also omitted if the function call is omitted. This creates an actual functional difference between placing #if DEBUG inside the method body and using the attribute.
  • Jai
    Jai over 4 years
    Personally, I don't see how this can be useful compared to the other 2 alternatives. This guarantees that the whole block is compiled, and Debugger.IsAttached must be called at runtime even in release builds.
  • cesAR
    cesAR almost 4 years
    I think add Debugger.IsAttached to this as third option would be great :) Thanks for your great explination!
  • Mickael Bergeron Néron
    Mickael Bergeron Néron over 2 years
    I strongly disagree. Something I sometimes do and consistently found useful is wrapping a validation with a throw inside #if DEBUG and handling the situation without crashing in the #else (optionally with sending us an exception email). For instance, let say a method parameter should not be null, then throwing if it's null inside #if DEBUG but assigning a default value and sending an exception email to us inside the #else.