C#和#x27;是';对结构的类型检查-奇数.NET 4.0 x86优化行为

C#和#x27;是';对结构的类型检查-奇数.NET 4.0 x86优化行为,c#,optimization,compiler-construction,casting,struct,C#,Optimization,Compiler Construction,Casting,Struct,更新:我已经向Microsoft Connect提交了一份申请,请投赞成票 更新2:Microsoft已将错误报告标记为已修复 微软于2010年8月18日17:25发布 此错误将在一个小时内修复 运行时的未来版本。我是 恐怕现在说这个还为时过早 将在service pack或下一个service pack中 主要版本 自从升级到VS2010后,我在“is”关键字上遇到了一些非常奇怪的行为 下面的程序(test.cs)在调试模式下编译时输出True(对于x86),在使用优化进行编译时输出False

更新:我已经向Microsoft Connect提交了一份申请,请投赞成票

更新2:Microsoft已将错误报告标记为已修复

微软于2010年8月18日17:25发布

此错误将在一个小时内修复 运行时的未来版本。我是 恐怕现在说这个还为时过早 将在service pack或下一个service pack中 主要版本

自从升级到VS2010后,我在“is”关键字上遇到了一些非常奇怪的行为

下面的程序(test.cs)在调试模式下编译时输出True(对于x86),在使用优化进行编译时输出False(对于x86)。在x64或AnyCPU中编译所有组合将得到预期结果True

在.NET3.5下编译的所有组合都给出了预期的结果,True

我正在使用下面的批处理文件(runtest.bat)使用编译器.NET framework的各种组合编译和测试代码

  • 还有人在.NET4.0下见过这种问题吗
  • 其他人在运行runtests.bat时是否在他们的计算机上看到与我相同的行为
  • @$@#$??
  • 有解决办法吗
test.cs runtest.bat 测试结果 在运行runtest.bat时,我在Win7 x64安装中得到以下结果

> runtest 32 v4.0
Compiler: Framework\v4.0.30319\csc.exe
False => x86
False => x86 (Optimized)
True => x86 (Debug)
False => x86 (Debug + Optimized)
True => x64
True => x64 (Optimized)
True => x64 (Debug)
True => x64 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)

> runtest 64 v4.0
Compiler: Framework64\v4.0.30319\csc.exe
False => x86
False => x86 (Optimized)
True => x86 (Debug)
False => x86 (Debug + Optimized)
True => x64
True => x64 (Optimized)
True => x64 (Debug)
True => x64 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)

> runtest 32 v3.5
Compiler: Framework\v3.5\csc.exe
True => x86
True => x86 (Optimized)
True => x86 (Debug)
True => x86 (Debug + Optimized)
True => x64
True => x64 (Optimized)
True => x64 (Debug)
True => x64 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)

> runtest 64 v3.5
Compiler: Framework64\v3.5\csc.exe
True => x86
True => x86 (Optimized)
True => x86 (Debug)
True => x86 (Debug + Optimized)
True => x64
True => x64 (Optimized)
True => x64 (Debug)
True => x64 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)

tl;dr除了一些NOP,reflector指出唯一的区别在于IsGuid方法:

调试:

.method public hidebysig static bool IsGuid(object item) cil managed
{
    .maxstack 2
    .locals init (
        [0] bool CS$1$0000)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: isinst [mscorlib]System.Guid
    L_0007: ldnull 
    L_0008: cgt.un 
    L_000a: stloc.0 
    L_000b: br.s L_000d
    L_000d: ldloc.0 
    L_000e: ret 
}
发布:

.method public hidebysig static bool IsGuid(object item) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: isinst [mscorlib]System.Guid
    L_0006: ldnull 
    L_0007: cgt.un 
    L_0009: ret 
}
我不能流利地解释这些差异,但这是一个社区维基答案,所以也许其他人可以启发我们

改变方法使其通用化可以解决这个问题(可能的bug?)


以下是我在XP SP3(x86)上的结果:


这个结果的有趣之处在于.NET 4目标在任何CPU(以x86运行)上失败,但在任何CPU(以x64运行)上工作。

这并不酷。我认为你应该提交一个bug。(connect.microsoft.com)

此外,这似乎是可行的(不过我只测试了你的失败案例):


要回答上一个问题,您可以在
IsGuid
方法中添加
MethodImpl
属性和选项
methodimpoptions.noinline
,作为解决问题的解决方法

我刚刚做了一个简单的测试,在.NET4.0上为x86在调试和发布配置之间切换,这似乎解决了问题。不过我还没有运行你的runtests.bat


如果一个问题尚未提交,您还应在中提交一个问题,并将其与您的问题链接起来。

我设计了一个类似的例子,但以同样的方式失败:

using System;
using System.Runtime.CompilerServices;

public class Program {
  static void Main() {
    Console.Write(Verify(Test.Create()));
    Console.ReadLine();
  }
  //[MethodImpl(MethodImplOptions.NoInlining)]
  static bool Verify(IDisposable item) {
    return item is Test;
  }
  struct Test : IDisposable {
    public void Dispose() { }
    public static Test Create() { return new Test(); }
  }
}
这是一个JIT优化器错误。不能完全把手指放在它上面,它极大地优化了代码。但在我看来,当它优化拳击转换时,它会遇到麻烦。坦白说,这是一个相当严重的错误



这个错误已经修复,我不能再重新编程了。我当前的clrjit.dll版本是4.0.30319.237,日期为2011年5月17日。我不知道到底是什么更新修复了它。我在2011年8月5日得到了一个安全更新,将clrjit.dll更新为235版,最早的日期是4月12日。

有趣的是,这是正确的:

using System;
public class Program
{
    public static bool IsGuid(object item)
    {
        return item is Guid;
    }

    public static void Main()
    {
        Guid s = Guid.NewGuid();
        Console.Write(IsGuid(s));
    }
}
以下是il的区别:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       23 (0x17)
  .maxstack  1
  .locals init (valuetype [mscorlib]System.Guid V_0)
  IL_0000:  call       valuetype [mscorlib]System.Guid [mscorlib]System.Guid::NewGuid()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  box        [mscorlib]System.Guid
  IL_000c:  call       bool Program::IsGuid(object)
  IL_0011:  call       void [mscorlib]System.Console::Write(bool)
  IL_0016:  ret
} // end of method Program::Main

好奇。我看到了同样的行为,但目前无法解释。很有趣。这种行为并不局限于guid,是吗?这肯定是一个bug。提交一份bug报告(确保给他们这个页面的链接和描述),看起来是更新解决了这个问题;请参阅“问题2”。我想知道它是否与JIT编译器有关?我现在有更多的问题需要回答:)IL很好。您要比较的是jitted代码,因为它以不同的抖动生成不同的结果。这看起来像是一个jit错误。要查看jit代码,最好编写一个程序,在要检查的代码运行后暂停。运行它,然后在暂停时连接调试器。之所以会出现这种僵硬的情况,是因为抖动知道何时连接了调试器,如果有调试器,它可以生成更容易调试的代码。如果你想知道它在没有调试器的情况下做什么,那么你必须欺骗它。它也总是适用于我自己的空值类型。如果参数为“new Guid()”,则它也始终适用于“is Guid”。此时,我怀疑.NET 4.0 32位运行时的Guid.NewGuid()实现;Console.Write(IsGuid(a));似乎有效。+1有趣的是,NoInling解决了这个问题。对于我们来说,这并不是一个真正的选择,因为我们的代码库中可能有数百个这样的案例,跟踪它们将很困难。
MethodImpl
在我的案例中不起作用。我在做
是在事件处理程序内部检查,我看到的不一致不是与
结构不一致,而是与一个适当的类(跨越AppDomain边界)。@asbjornu,考虑到这些差异,你应该问一个新问题,包括所有细节。ão,是的,一旦我能够完全了解正在发生的事情,我会的。更多信息,请参见此:
public static bool IsGuid(object item)
{
    bool a = item is Guid;
    a.ToString();
    return a;
}
>runtest
Compiler: Framework\v4.0.30319\csc.exe
False => x86
False => x86 (Optimized)
True => x86 (Debug)
False => x86 (Debug + Optimized)
False => AnyCPU
False => AnyCPU (Optimized)
True => AnyCPU (Debug)
False => AnyCPU (Debug + Optimized)

>runtest v3.5
Compiler: Framework\v3.5\csc.exe
True => x86
True => x86 (Optimized)
True => x86 (Debug)
True => x86 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)
    public static bool IsGuid(object item)
    {
        return item.GetType() == typeof(Guid);
    }
using System;
using System.Runtime.CompilerServices;

public class Program {
  static void Main() {
    Console.Write(Verify(Test.Create()));
    Console.ReadLine();
  }
  //[MethodImpl(MethodImplOptions.NoInlining)]
  static bool Verify(IDisposable item) {
    return item is Test;
  }
  struct Test : IDisposable {
    public void Dispose() { }
    public static Test Create() { return new Test(); }
  }
}
using System;
public class Program
{
    public static bool IsGuid(object item)
    {
        return item is Guid;
    }

    public static void Main()
    {
        Guid s = Guid.NewGuid();
        Console.Write(IsGuid(s));
    }
}
.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       23 (0x17)
  .maxstack  1
  .locals init (valuetype [mscorlib]System.Guid V_0)
  IL_0000:  call       valuetype [mscorlib]System.Guid [mscorlib]System.Guid::NewGuid()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  box        [mscorlib]System.Guid
  IL_000c:  call       bool Program::IsGuid(object)
  IL_0011:  call       void [mscorlib]System.Console::Write(bool)
  IL_0016:  ret
} // end of method Program::Main