发布和调试之间的不同C#输出

时间:2015-04-14 19:54:19

标签: c# debugging release language-lawyer

我在这里有一个小的C#程序,它在Debug和Release版本之间产生不同的输出。我认为,Release版本的空输出与C#语言规范一致。这个程序不应该产生输出,但是Debug版本会产生输出。

我从命令行(在VS环境之外)运行Release和Debug版本,并获得相同的不一致输出。我已经反编译了Debug版本(使用ILDASM),然后用ILASM重新编译它。当我这样做时,新编译的程序就像Release版本一样。我只能想象,当我解编译然后重新编译时,某些东西被遗漏了,但我无法确定有什么不同。

关于EXE文件大小:VS生成的Release版本和Debug版本具有相同的文件大小:5,120字节。当我解编译并重新编译时,两个版本再次具有相同但较小的文件大小:3,072。

该程序非常小,我在Reflector中查看了IL,我看不到任何会导致输出差异的内容。

有没有人(希望详细)解释为什么会有区别?

请注意,我不是尝试使调试和发布版本保持一致,我想知道它们为什么不一致。

回想一下我上面所说的 - 即使从命令行运行,Debug和Release版本也会产生不同的输出。如果你告诉我运行时正在对Release版本进行某种优化而不是对Debug版本进行某种优化,那么Debug / Release版本程序集中必须嵌入一些东西,告诉运行时打开/关闭优化。什么是嵌入式“某些东西”以及为什么在使用ILDASM / ILASM时它不会延续?

以下是代码:

using System;

class Test {
    static int value = 0;
    static int a = Initialize("Assigning a");
    static int b = Initialize("Assigning b");
    static String name = "Fred";
    static int c = Initialize("Assigning c");

    static int Initialize(String mssg) {
        ++value;
        Console.WriteLine("In Initialize() :: {0}, name={1}, returning {2}", mssg, name, value);
        return value;
    } // Initialize()

    static void Main() {
    } // Main()
} // class Test

以下是Visual Studio生成的Debug版本的输出:

In Initialize() :: Assigning a, name=, returning 1
In Initialize() :: Assigning b, name=, returning 2
In Initialize() :: Assigning c, name=Fred, returning 3

运行发布版本不会生成输出。

4 个答案:

答案 0 :(得分:2)

在需要之前不会调用静态类初始值设定项。很明显,调试和发布版本在需要时决定了不同的方式。特别是我的猜测是发布版本完全优化了主要版本,所以永远不会加载类。它似乎已经决定,因为main什么都不做,它可以优化一切 - 在这种情况下,它似乎是一个糟糕的决定

答案 1 :(得分:2)

启用优化(发布模式)会影响生成的IL以及JITter的行为。

您可能正在看到JITter消除了未使用变量的初始化。

这解释了ILDASM / ILASM的行为,以及IL没有显着差异的事实。

我怀疑此行为是由CLR标头中某处的CorDebugJITCompilerFlags标志的值控制的。请参阅 Does C# compiler /optimize command line option affect JITter?

答案 2 :(得分:2)

经过更多的研究,我找到了我想要的答案(感谢Blogbeard指出我正确的方向)。

事实证明,当你为Debug编译时,默认情况下,生成的程序集用DebuggableAttribute修饰,其“调试模式”是

DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.Default

显然这种标志组合似乎关闭了JIT优化,导致我在程序的Debug版本中看到的输出。该程序的Release版本具有不同的“调试模式”,允许JIT优化继续进行。

当然,如果您在Debug构建中手动设置AssemblyInfo中的DebuggableAttribute(就像我在测试期间所做的那样),您可以覆盖默认行为。

我确信有一些CLR / JIT律师可以更详细地解释。

答案 3 :(得分:0)

你实际上并没有在应用程序中使用变量a,b或c(在Main中),所以我假设它们正在被优化掉。当我在LinqPad中运行该代码并关闭优化时,它会显示您描述的输出,并且在进行优化时,除了执行Main()之外,它只显示其他内容。如果我更新Main()中的代码以引用其中一个变量,则输出与构建时的优化一致。