相同的IL代码,不同的输出 - 如何可能?

时间:2010-05-10 22:40:56

标签: c# .net mono cil

我有一段代码,它根据C#编译器和运行时输出不同的结果。

有问题的代码是:

using System;

public class Program {
    public static void Main() {
        Console.WriteLine(string.Compare("alo\0alo\0", "alo\0alo\0\0", false, System.Globalization.CultureInfo.InvariantCulture));
    }
}

结果是:

                    Compiling with mono (gmcs)    Compiling with .Net (csc)
Running with mono                           -1                           -1
Running with .Net                           -1                            0

使用.Net框架运行时,如何输出不同的值?

(顺便说一下,根据http://msdn.microsoft.com/en-us/library/system.string.aspx,输出应为0,因此单声道的答案不正确,但这与我的问题无关。)

即使生成的IL代码也(几乎)相同。

使用.Net进行编译:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       29 (0x1d)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      bytearray (61 00 6C 00 6F 00 00 00 61 00 6C 00 6F 00 00 00 ) // a.l.o...a.l.o...
  IL_0006:  ldstr      bytearray (61 00 6C 00 6F 00 00 00 61 00 6C 00 6F 00 00 00   // a.l.o...a.l.o...
                                  00 00 ) 
  IL_000b:  ldc.i4.0
  IL_000c:  call       class [mscorlib]System.Globalization.CultureInfo [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture()
  IL_0011:  call       int32 [mscorlib]System.String::Compare(string,
                                                              string,
                                                              bool,
                                                              class [mscorlib]System.Globalization.CultureInfo)
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_001b:  nop
  IL_001c:  ret
} // end of method Program::Main

使用mono进行编译:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       27 (0x1b)
  .maxstack  8
  IL_0000:  ldstr      bytearray (61 00 6C 00 6F 00 00 00 61 00 6C 00 6F 00 00 00 ) // a.l.o...a.l.o...
  IL_0005:  ldstr      bytearray (61 00 6C 00 6F 00 00 00 61 00 6C 00 6F 00 00 00   // a.l.o...a.l.o...
                                  00 00 ) 
  IL_000a:  ldc.i4.0
  IL_000b:  call       class [mscorlib]System.Globalization.CultureInfo [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture()
  IL_0010:  call       int32 [mscorlib]System.String::Compare(string,
                                                              string,
                                                              bool,
                                                              class [mscorlib]System.Globalization.CultureInfo)
  IL_0015:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_001a:  ret
} // end of method Program::Main

唯一的区别是.Net版本中的两个额外NOP指令。

怎么可能?两个输出值如何不同?

另外,如果有人同时安装了.Net和单声道,你可以重现它吗?

编辑:我不在乎正确的结果是什么,我不在乎单声道和.Net会产生不同的结果。我可能永远不会遇到嵌入的空值并对它们进行排序,排序顺序很重要。

我的问题是,当不同的编译器编译时,相同的运行时(。Net 2.0)会产生不同的结果。

编辑2:我添加了一张表并尝试澄清问题,现在应该更容易理解。

5 个答案:

答案 0 :(得分:3)

我的猜测是当你用Mono编译它时,它引用了mscorlib的.NET 2.0版本 - 而当你用VS编译它时,它的目标是.NET 4.0。

我可能不确定在每种情况下针对哪个确切版本,但这是我要开始的地方:不要查看IL的方法,查看引用的程序集。

(如果你说你安装了哪个版本的VS,.NET和Mono,顺便说一句,可能会有所帮助。)

编辑:好的,所以如果无论你的目标是什么版本都会做同样的事情,那么在每个版本上运行ildasm的结果上运行差异怎么样?比较整个文件,而不仅仅是方法调用本身的IL。

答案 1 :(得分:2)

这与字符串排序有关吗?请参阅Marc的this博客条目

答案 2 :(得分:1)

我认为这只是另一个Mono与.NET的不兼容性(特别是在处理文化信息时在System.String :: Compare中),所以请记录一个报告以通知Novell / Mono团队。他们可以反馈并确认是否需要。如果这是一个错误,你至少知道什么时候可以修复。

http://www.mono-project.com/Bugs

答案 3 :(得分:0)

字符串比较由CultureInfo.CompareInfo确定。我的猜测是CultureInfo.CompareInfo在.Net与Mono上返回不同的位掩码。当我打印0xff时,.Net 3.5返回System.Globalization.CultureInfo.InvariantCulture.CompareInfo。在Mono下打印什么?将缺失位与System.Globalization.CultureInfo.CompareOptions进行比较。如果位掩码相同,则Mono或.Net中的文化信息如何被解释。

考虑到这一点,如果反汇编是相同的话,我会非常注意你的代码中的任何可观察的差异。

答案 4 :(得分:0)

我猜想nop(No Operation)asm命令用于在内存中对齐命令。这使CPU有机会将代码加载到内部缓存中,从而更快地运行它。这是非常标准的优化技术,似乎.NET编译器在Mono不关心的情况下完成它。