Search code examples
c#.netdebuggingcompilationconditional-compilation

C# library method with [Conditional("RELEASE")] still called in Debug build


I have the following code in MyLib.dll:

    [Obsolete("This method is for debugging only. Remove all calls to this method once done.")]
    public void SetLaunchCount(int count)
    {
        ThrowOnReleaseBuild();
        launchEntry.Set(count);
    }

    [Conditional("RELEASE")]
    static void ThrowOnReleaseBuild()
    {
        throw new InvalidOperationException("This method must not be called outside of DEBUG build"); ;
    }

When I tested it using a project (MAUI in my case) in the same solution, it works fine. However, when packing it and uploading it to Nuget (through our private Github Nuget feed), calling SetLaunchCount always throw and the stack trace shows call to ThrowOnReleaseBuild.

I have double checked and the Debug build indeed has DEBUG symbol (and more precisely, no RELEASE symbol):

enter image description here

I also tried adding another Debug.WriteLine method to confirm it:

enter image description here

Why is it not working? I checked the (decompiled) source code of Debug.WriteLine for example, and it's coded just like my method:

        [Conditional("DEBUG")]
        public static void WriteLine(string? message) =>
            s_provider.WriteLine(message);

EDIT: adding some more information:

  • The library was built in Release profile.

  • See this answer on my previous question about using Conditional. Somehow it does not work now.


Solution

  • It works as expected. From the documentation you can read (emphasis mine) that

    Applying ConditionalAttribute to a method indicates to compilers that a call to the method should not be compiled into Microsoft intermediate language (MSIL) unless the conditional compilation symbol that is associated with ConditionalAttribute is defined.

    So when you use Debug.WriteLine and you compile your code with RELEASE or DEBUG flag, it's not that the code inside the Debug.WriteLine is removed. It's the call itself that's not there - that's why "it works".

    So in your case: In DEBUG mode the SetLaunchCount method is affected in the following way:

    [Obsolete("This method is for debugging only. Remove all calls to this method once done.")]
    public void SetLaunchCount(int count)
    {
        ThrowOnReleaseBuild(); <-- this is removed in DEBUG, and not in RELEASE
        launchEntry.Set(count);
    }
    

    When you pack this into NuGet in Release mode, the method is there and if you compile your code in DEBUG or RELEASE doesn't affect it as it's not your code that calls the method that is marked as Conditional attribute. You only call SetLaunchCount.

    If you would make the method ThrowOnReleaseBuild public and call it from your code, even if it would be in the Nuget - it would work as you expect as the call to that method would be there or not depending on the condition.