Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this program enter the if block in Release build but not in Debug build?

I have some code that behaves differently between a Release build and a Debug build. It behaves correctly in Debug but not in Release.

I have a function that returns a ReadOnlyCollection<MyCustomClass>. One section is this:

        var result = new List<MyCustomClass>();
        ...
        var list1 = this.returnEmptyList();
        var list2 = this.returnListWithOneItem();
        if (list1.Count == 0 && list2.Count == 0)
        {
            functionOutVariable = string.Empty;
            return result.AsReadOnly();
        }

For the purposes of troubleshooting I've simplified the code and named the variables in a generic fashion, and the methods returnEmptyList and returnListWithOneItem are shown here:

    private List<string> returnEmptyList()
    {
        return new List<string>();
    }

    private List<string> returnListWithOneItem()
    {
        return new List<string> {"something"};
    }

Clearly it should never enter the if block because list2.Count should always be 1, but when I execute this in a Release build, it does:

enter image description here

So there's clearly some optimization going on since you can see that list1 is inaccessible, and when stepping through it executed line 416 and then jumped immediately to line 421. I should state that all the assemblies in my solution use .NET Framework 4.6.2, and I'm running Visual Studio 2017 version 15.3.5.

When I change the build to Debug and execute this, it executes line 416, 417, and on line 418 it shows list1.Count is 0 and list2.Count is 1, and it correctly does not enter the if block.

I'm trying to make a test project to reproduce this but I can't. I'm looking for any way to get to the bottom of this. I don't just want a fix that makes it go away - I need to understand what I'm doing wrong.

like image 842
Scott Whitlock Avatar asked Oct 20 '17 13:10

Scott Whitlock


People also ask

What is the difference between a debug build and a release build?

Release vs Debug and it depends on what language you are using, Debug includes debugging information in the compiled files (allowing easy debugging) while Release usually has optimizations enabled. They each define different symbols that can be checked in your program, but they are language-specific macros.

What is the difference between debug and release mode?

Debug Mode: In debug mode the application will be slow. Release Mode: In release mode the application will be faster. Debug Mode: In the debug mode code, which is under the debug, symbols will be executed. Release Mode: In release mode code, which is under the debug, symbols will not be executed.

How do I change debug to release?

Change the build configurationOn the toolbar, choose either Debug or Release from the Solution Configurations list. From the Build menu, select Configuration Manager, then select Debug or Release.


1 Answers

Ok, I'm pretty sure it's a result of a subtle bug in the rest of my function that allowed the compiler to just optimize out the if block and return early. I can reproduce the behavior of the debugger in this test project, and it totally makes sense in this case:

class Program
{
    static void Main(string[] args)
    {
        var test = new MyClass();
        test.DoTest(out var result);
        Console.WriteLine(result);
        Console.ReadKey();
    }
}

class MyClass
{
    public ReadOnlyCollection<MyCustomClass> DoTest(out string functionOutVariable)
    {
        var result = new List<MyCustomClass>();
        var list1 = this.returnEmptyList();
        var list2 = this.returnListWithOneItem();
        if (list1.Count == 0 && list2.Count == 0)
        {
            functionOutVariable = string.Empty;
            return result.AsReadOnly();
        }
        functionOutVariable = string.Empty;
        return result.AsReadOnly();
    }

    private List<string> returnEmptyList()
    {
        return new List<string>();
    }

    private List<string> returnListWithOneItem()
    {
        return new List<string> { "something" };
    }
}

class MyCustomClass
{

}

When I execute in a Release build with the debugger, it appears to enter the if block, but in reality it just optimized out the if block completely and the debugger confusingly shows it executing the lines inside the if block instead of jumping over it:

enter image description here

Edit: I've confirmed there was a bug later in the function that was causing my problem, and the behavior of the debugger when looking at Release build code was just causing me confusion, due to compiler optimizations.

To be clear, my question is incorrect: the function was actually giving the same result in both Release and Debug builds but I was mistaken. That's because I followed this (flawed) sequence:

  1. I had a failing test (against a Release build).
  2. I ran the test with the debugger (still in Release build) and saw it apparently go into the if block incorrectly.
  3. I then switched the build to Debug and ran the test with the debugger and saw it step over the if block. I assumed (incorrectly) that was the source of my problem.

That sent me on the wild goose chase seen above. Sorry for wasting your time, but I certainly found the exercise informative. Perhaps someone else will learn from my mistake in the future. :)

like image 118
Scott Whitlock Avatar answered Oct 26 '22 11:10

Scott Whitlock