为什么这个.NET IL空检查没有按预期工作?

问题描述:

我正在写一些自定义IL,需要一些相当于return SomeStaticField != null;的东西。这是我的自然结论:为什么这个.NET IL空检查没有按预期工作?

volatile.ldsfld ...SomeStaticField //volatile is needed here for unrelated reasons 
ldnull 
ceq 
not 
ret 

但是,这似乎并不奏效。我确认SomeStaticField为null,但是这个函数最终返回true。我知道C#使用分支这样的结构,我可以使用它,但它令我感到困惑,为什么这不会有预期的行为

一个完整的和可核查的例子(如图书馆):

.assembly extern /*23000001*/ mscorlib 
{ 
    .publickeytoken = (B7 7A 5C 56 19 34 E0 89)       // .z\V.4.. 
    .ver 4:0:0:0 
} 
.assembly 'BareMetal' 
{ 
    .custom instance void class [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::'.ctor'() = (
       01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 
       63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01  ) // ceptionThrows. 

    .hash algorithm 0x00008004 
    .ver 1:0:0:0 
} 
.module BareMetal.dll 


.namespace Earlz.BareMetal 
{ 
    .class public auto ansi abstract sealed beforefieldinit BareMetal 
     extends [mscorlib]System.Object 
    { 
    .method public static hidebysig 
      default bool FooTest() cil managed 
    { 
     .maxstack 2 
     ldnull 
     ldnull 
     ceq 
     not 
     ret 
    } 

    } 
} 
+0

它应该工作,[CEQ绝对是用于null检查(http://stackoverflow.com/questions/24023705/when-i-use-is-operator-why-there-is-only -a无效签入IL-代码)。请提供[mcve]。 – Heinzi

+1

'不'不是。总是最好让C#编译器先生成msil,然后用ildasm.exe来查看它。几乎没有人认为使用'cgt.un'。 –

+0

@Heinzi我更新了一个完整的例子(为简单起见用ldnull替换了ldsfld) – Earlz

not计算其操作数的按位补码:~1-2,它是非零的,为真。否定布尔值的规范方法是ldc.i4.0 ; ceq

而且,正如@HansPassant指出的那样,直接进行!= null比较的规范方式是cgt.un - 当解释为无符号值时,所有有效的引用都是“大于零”。这种比较是在安全ECMA-335被明确记载,部分I.12.1.5:

特别地,对象引用可以是:

...

  1. 创建为空引用(ldnull
  2. ...

    托管指针有几个额外的基地操作。

    ...

      基于两个
    1. 无符号的比较和条件分支管理指针(bge.unbge.un.sbgt.unbgt.un.sble.unble.un.sblt.unblt.un.scgt.unclt.un)。
    开始=>
开始=>
+0

我一直使用'brtrue'或'brfalse'做空检查,因为我不必为了比较而做'ldnull'。这种情况下有什么不同,使比较版本更好? – Kyle

+1

@凯尔:不。如果你需要比较的结果作为布尔值,无论出于何种原因(也许将表达式组合并稍后保存在分支上),这就是你如何做到的。如果你想立即分支,'brtrue'和'brfalse'确实是这样做的常用方式(事实上,'brinst'和'brnull'是别名的常见做法)。当然,你也可以分支和加载一个常量布尔值;什么更好取决于你在做什么以及JIT编译器最终产生什么。 –