我偶然发现C#编译器将此方法转换为
static bool IsNotNull(object obj)
{
return obj != null;
}
…进入这个CIL:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0 // obj
ldnull
cgt.un
ret
}
…或者,如果你更喜欢看反编译的C#代码:
static bool IsNotNull(object obj)
{
return obj > null; // (note: this is not a valid C# expression)
}
为什么!=
被翻译成">
"?
简短回答:
不存在";比较不相等";指令,因此C#!=
运算符没有精确的对应关系,无法从字面上翻译。
然而,存在一个";比较相等";指令(ceq
,与==
运算符的直接对应关系),因此在一般情况下,x != y
会像其稍长的等价(x == y) == false
一样被翻译。
还有也一个";比较大于"0";IL(cgt
)中的指令,它允许编译器采用某些快捷方式(即生成较短的IL代码),其中之一是对象与null(obj != null
)的不等式比较被转换为"零";CCD_ 10";。
让我们更详细地介绍一下
如果不存在";比较不相等";指令,那么编译器将如何翻译以下方法?
static bool IsNotEqual(int x, int y)
{
return x != y;
}
如上所述,编译器将把x != y
转换为(x == y) == false
:
.method private hidebysig static bool IsNotEqual(int32 x, int32 y) cil managed
{
ldarg.0 // x
ldarg.1 // y
ceq
ldc.i4.0 // false
ceq // (note: two comparisons in total)
ret
}
事实证明,编译器并不总是产生这种冗长的模式。让我们看看当我们用常数0:替换y
时会发生什么
static bool IsNotZero(int x)
{
return x != 0;
}
产生的IL比一般情况下更短:
.method private hidebysig static bool IsNotZero(int32 x) cil managed
{
ldarg.0 // x
ldc.i4.0 // 0
cgt.un // (note: just one comparison)
ret
}
编译器可以利用有符号整数存储在2的补码中的事实(其中,如果生成的位模式被解释为无符号整数——这就是.un
的意思——0的值可能最小),因此它将x == 0
转换为unchecked((uint)x) > 0
。
事实证明,编译器可以对null
:进行不等式检查
static bool IsNotNull(object obj)
{
return obj != null;
}
编译器生成与IsNotZero
:几乎相同的IL
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0
ldnull // (note: this is the only difference)
cgt.un
ret
}
显然,编译器可以假设null
引用的位模式是任何对象引用可能的最小位模式。
通用语言基础设施注释标准(2003年10月第1版)(第491页,作为表6-4"二进制比较或分支运算"的脚注)中明确提到了这种快捷方式:
";在ObjectRefs(O)上允许并可验证
cgt.un
。这通常用于将ObjectRef与null进行比较(没有"compare not equal"指令,否则这将是一个更明显的解决方案)"