为什么这个程序在Release版本中输入if块而在Debug build中没有?

Sco*_*ock 9 c# compiler-optimization

我有一些代码在Release版本和Debug版本之间表现不同.它在Debug中正常运行,但在Release中不正常.

我有一个函数返回一个ReadOnlyCollection<MyCustomClass>.一节是这样的:

        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();
        }
Run Code Online (Sandbox Code Playgroud)

出于故障排除的目的,我简化了代码并以通用方式命名变量,方法returnEmptyList和returnListWithOneItem方法如下所示:

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

    private List<string> returnListWithOneItem()
    {
        return new List<string> {"something"};
    }
Run Code Online (Sandbox Code Playgroud)

显然它永远不应该进入if块,因为它list2.Count应该始终为1,但是当我在Release版本中执行它时,它会:

在此输入图像描述

因此,显然有一些优化正在进行,因为您可以看到它list1是不可访问的,并且当单步执行它时执行第416行然后立即跳转到第421行.我应该声明我的解决方案中的所有程序集都使用.NET Framework 4.6.2,并且我正在运行Visual Studio 2017版本15.3.5.

当我改变构建调试和执行这一点,执行管线416,417,和上线418它显示list1.Count为0和list2.Count1,并正确并不能进入if块.

我正在尝试制作一个测试项目来重现这一点,但我不能.我正在寻找任何方法来深究这一点.我不仅仅想要一个让它消失的修复 - 我需要了解我做错了什么.

Sco*_*ock 7

好吧,我很确定这是我的函数其余部分中的一个微妙错误的结果,它允许编译器优化if块并提前返回.我可以在这个测试项目中重现调试器的行为,在这种情况下它完全有意义:

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
{

}
Run Code Online (Sandbox Code Playgroud)

当我在一个发布版本与调试器中执行,这似乎进入if块,但实际上它只是优化了if块完全和调试器混淆的显示它执行里面的线if,而不是跳过它块:

在此输入图像描述

编辑:我已经确认函数后面有一个错误导致了我的问题,并且在查看Release构建代码时调试器的行为只是由于编译器优化而导致我混淆.

要清楚,我的问题是不正确的:该函数实际上在Release和Debug版本中都给出了相同的结果,但我错了.那是因为我遵循了这个(有缺陷的)序列:

  1. 我有一个失败的测试(针对发布版本).
  2. 我使用调试器运行测试(仍在发布版本中)并且看到它显然if不正确地进入块.
  3. 然后我将构建切换到Debug并使用调试器运行测试并看到它跨越if块. 我假设(错误地)这是我的问题的根源.

这让我在上面看到了疯狂的追逐.很抱歉浪费你的时间,但我当然发现这项运动信息丰富.也许其他人将来会从我的错误中吸取教训.:)