为什么.NET IL null检查不能按预期工作?
我正在编写一些自定义IL,需要与为什么.NET IL null检查不能按预期工作?,.net,cil,.net,Cil,我正在编写一些自定义IL,需要与returnsomestaticfield!=无效。这是我自然得出的结论: volatile.ldsfld ...SomeStaticField //volatile is needed here for unrelated reasons ldnull ceq not ret 然而,这似乎不起作用。我确认SomeStaticField为null,但该函数最终将返回true。我知道C#使用分支来实现这样的构造,我也可以使用它,但这让我很困惑,为什么这不会产生预期
returnsomestaticfield!=无效代码>。这是我自然得出的结论:
volatile.ldsfld ...SomeStaticField //volatile is needed here for unrelated reasons
ldnull
ceq
not
ret
然而,这似乎不起作用。我确认SomeStaticField为null,但该函数最终将返回true。我知道C#使用分支来实现这样的构造,我也可以使用它,但这让我很困惑,为什么这不会产生预期的行为
完整且可验证的示例(作为库):
not
计算其操作数的位补码:~1
为-2
,非零为真。否定布尔值的标准方法是ldc.i4.0;ceq
正如@HansPassant所指出的,做!=null
直接比较是cgt.un
——当解释为无符号值时,所有有效引用都“大于零”。第I.12.1.5节中明确记录了此类比较的安全性:
具体而言,对象引用可以是:
创建为空引用(ldnull
)
托管指针还有几个附加的基本操作
基于两个托管指针的无符号比较和条件分支(bge.un
,bge.un.s
,bgt.un
,bgt.un.s
,ble.un
,
ble.un.s
,blt.un
,blt.un.s
,cgt.un
,clt.un
)
它应该会起作用。请提供一个。不是
不是吗。始终最好让C#编译器先生成msil,然后使用ildasm.exe查看它。几乎没有人想到使用cgt.un
@Heinzi I updated添加了一个完整的示例(为了简单起见,将ldsfld替换为ldnull)@HansPassant对null和引用类型使用cgt安全吗?我认为这只对数字有效你想针对C#编译器提交错误报告吗?呵呵。它是安全的。我一直使用brtrue
或brfalse
进行空值检查,因为我不必进行ldnull
比较。在这种情况下,比较版本更可取吗?@Kyle:不。如果出于任何原因(可能是为了组合表达式并稍后保存到分支上),需要将比较结果作为布尔值,那么您就可以这样做。如果您想立即分支,brtrue
和brfalse
确实是实现这一点的常用方法(事实上,brinst
和brnull
都是别名)。当然,您也可以分支并加载一个常量布尔值;什么更好取决于您正在做什么以及JIT编译器最终生成什么。
.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
}
}
}