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

Why is this .NET IL null check not working as expected?

我正在编写一些自定义 IL,需要与 return SomeStaticField != null; 等效的东西。这是我的自然结论:

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
    }

  }
}

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

而且,正如@HansPassant 指出的那样,直接进行 != null 比较的规范方法是 cgt.un——所有有效引用在被解释为无符号值时都是 "greater than zero"。 ECMA-335 部分 I.12.1.5:

中明确记录了此类比较是安全的

In particular, object references can be:

...

  1. Created as a null reference (ldnull)

...

Managed pointers have several additional base operations.

...

  1. Unsigned comparison and conditional branches based on two managed pointers (bge.un, bge.un.s, bgt.un, bgt.un.s, ble.un, ble.un.s, blt.un, blt.un.s, cgt.un, clt.un).