发布和调试之间的不同C#输出
我这里有一个小C#程序,它在调试版本和发布版本之间产生不同的输出。我认为,发布版本的空输出与C语言规范是一致的。此程序不应产生输出,但调试版本会产生输出 我从命令行(在VS环境之外)运行了发行版和调试版,得到了相同的不一致输出。我已经反编译了调试版本(使用ILDASM),然后用ILASM重新编译。当我这样做时,新编译的程序的行为就像发布版本一样。我只能想象,当我反编译然后重新编译时,有些东西被遗漏了,但我无法确定有什么不同 关于EXE文件大小:VS生成的发行版和调试版的文件大小相同:5120字节。当我反编译和重新编译时,两个版本的文件大小相同,但更小:3072 这个程序非常小,我已经看过反射器中的IL,我看不到任何会导致输出差异的东西 有人(希望详细)解释了为什么会有差异吗 请注意,我并没有试图使调试版本和发布版本保持一致,我想了解为什么它们不一致 回想一下我上面所说的——调试版本和发布版本即使在从命令行运行时也会产生不同的输出。如果您告诉我运行时正在对发布版本进行某种优化,而不是对调试版本进行优化,那么调试/发布版本程序集中一定嵌入了某种东西,告诉运行时打开/关闭优化。嵌入的“某物”是什么?为什么在使用ILDASM/ILASM时它不会继续 代码如下:发布和调试之间的不同C#输出,c#,debugging,release,language-lawyer,C#,Debugging,Release,Language Lawyer,我这里有一个小C#程序,它在调试版本和发布版本之间产生不同的输出。我认为,发布版本的空输出与C语言规范是一致的。此程序不应产生输出,但调试版本会产生输出 我从命令行(在VS环境之外)运行了发行版和调试版,得到了相同的不一致输出。我已经反编译了调试版本(使用ILDASM),然后用ILASM重新编译。当我这样做时,新编译的程序的行为就像发布版本一样。我只能想象,当我反编译然后重新编译时,有些东西被遗漏了,但我无法确定有什么不同 关于EXE文件大小:VS生成的发行版和调试版的文件大小相同:5120字节
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生成的调试版本的输出:
In Initialize() :: Assigning a, name=, returning 1
In Initialize() :: Assigning b, name=, returning 2
In Initialize() :: Assigning c, name=Fred, returning 3
运行发布版本不会生成任何输出。您实际上没有在应用程序(Main内部)的任何位置使用变量a、b或c,因此我假设它们正在被优化。当我在关闭优化的情况下在LinqPad中运行该代码时,它会显示您描述的输出,而在打开优化的情况下,它只显示Main()正在执行。如果我更新Main()中的代码以引用这些变量中的一个,则输出与优化外建一致。直到需要时才调用静态类初始值设定项。显然,调试版本和发布版本在需要时的决定是不同的。特别是我的猜测是,发布版本完全优化了主版本,因此从未加载该类。它似乎已经决定,因为main什么都不做,所以它可以优化所有东西-在这种情况下,这似乎是一个错误的决定打开优化(释放模式)会影响生成的IL,以及抖动的行为 您可能看到抖动消除了未使用变量的初始化 这解释了ILDASM/ILASM行为,以及IL之间没有显著差异的事实
我怀疑这种行为是由CLR头中某个地方的标志值控制的。。参见经过更多的研究,我找到了我想要的答案(感谢Blogbeard为我指明了正确的方向) 事实证明,当您编译以进行调试时,生成的程序集在默认情况下由“调试模式”为的DebuggableAttribute修饰 显然,正是这些标志的组合似乎关闭了JIT优化,导致了我在程序调试版本中看到的输出。程序的发布版本有不同的“调试模式”,允许JIT优化继续进行 当然,如果为调试生成在AssemblyInfo中手动设置DebuggableAttribute(就像我在测试期间所做的那样),则可以覆盖默认行为
我确信有一些CLR/JIT律师可以提供更详细的解释。最有可能的是,在发布模式下,测试类未加载,因为没有对它的引用。尝试添加
Console.WriteLine(Test.name)我建议在main方法中放置一个Console.WriteLine(“在main中”)代码>以显示实际上正在调用main。(我刚刚试过,您仍然可以得到不同的输出,但是如果您做得更多,例如Console.WriteLine(“在Main():{0}”,name);
,然后调试并释放,开始表现相同)分页skeet先生,skeet先生如果您愿意,请来到SO桌面,我发现了如何禁用调试优化,以便您仍然可以在调试器中看到不同的行为(如果您选中“启用地址级别ebugging”复选框,您可以从Disassemby窗口中看到汇编代码)。我知道发生了什么(JITed代码不调用其他静态方法),但我不知道为什么会发生这种情况,所以我不打算发布一个答案,只是猜测代码>将在不调用静态函数的情况下打印。所以主要的并没有被完全淘汰,所以我明白了。我想只是取消了未使用变量的初始化。“特别是我的猜测是,发布版本完全优化了main out,因此从未加载该类。”事实并非如此,putConsole.WriteLine(“IN main()”)代码>在main中,您仍然会得到相同的行为,但它将打印“in main()”
DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.Default