简短的回答:
IL 中没有“比较不等于”指令,因此 C#!=
运算符没有精确对应关系,无法直译。
然而,有一个“比较相等”指令(ceq
,直接对应于==
运算符),因此在一般情况下,x != y
它被翻译成稍长的等效指令(x == y) == false
。
在 IL ( )中还有一个“比较大于”指令,cgt
它允许编译器采用某些快捷方式(即生成更短的 IL 代码),一个是对象与 null 的不等比较obj != null
,被翻译为好像它们是“ obj > null
”。
让我们更详细地介绍一下。
如果 IL 中没有“比较不等于”指令,那么编译器将如何翻译以下方法?
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
}
事实证明,编译器并不总是产生这种相当冗长的模式。让我们看看当我们y
用常量 0 替换时会发生什么:
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
}
编译器可以利用有符号整数存储在二进制补码中这一事实(其中,如果生成的位模式被解释为无符号整数 - 这就是.un
意思 - 0 具有最小可能值),所以它转换x == 0
为好像它是unchecked((uint)x) > 0
.
事实证明,编译器可以对不等式检查做同样的事情null
:
static bool IsNotNull(object obj)
{
return obj != null;
}
编译器产生与 for 几乎相同的 IL IsNotZero
:
.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 “二进制比较或分支操作”的脚注):
"cgt.un
在 ObjectRefs (O) 上是允许和可验证的。这通常在将 ObjectRef 与 null 进行比较时使用(没有“比较不等于”指令,否则这将是一个更明显的解决方案)。