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

时间:2017-10-20 13:34:32

标签: 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();
        }

为了排除故障,我简化了代码并以通用方式命名变量,方法returnEmptyListreturnListWithOneItem如下所示:

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

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

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

enter image description here

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

当我将构建更改为Debug并执行此操作时,它会执行第416,417行,并且在第418行显示list1.Count为0且list2.Count为1,并且它正确执行输入if块。

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

1 个答案:

答案 0 :(得分: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
{

}

当我使用调试器在Release版本中执行时,它似乎进入if块,但实际上它只是完全优化了if块并且调试器容易混淆地显示它执行行在if块内,而不是跳过它:

enter image description here

编辑:我已经确认函数后面有一个错误导致了我的问题,调查器在查看发布版本代码时的行为只是让我感到困惑,由于编译器优化。

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

  1. 我的测试失败(针对发布版本)。
  2. 我使用调试器运行测试(仍处于发布版本中)并且看到显然错误地进入if块。
  3. 然后我将构建切换到Debug并使用调试器运行测试并看到它跨过if块。 我认为(错误地)是我问题的根源。
  4. 这让我在上面看到了疯狂的追逐。很抱歉浪费你的时间,但我当然发现这项运动信息丰富。也许其他人将来会从我的错误中吸取教训。 :)