简答:
IL中没有“比较不等于”指令,所以C#!=操作符没有精确对应关系,不能直译。
但是有一个“比较相等”指令(ceq,直接对应于 == 运算符),所以在一般情况下,x != y 被翻译成稍长的等效 (x == y) == false。
还有在 IL (cgt) 中有一个“compare-greater-than”指令,它允许编译器采用某些快捷方式(即生成更短的 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
}
事实证明,编译器并不总是产生这种相当冗长的模式。让我们看看当我们用常量 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
}
编译器可以利用有符号整数存储在two's complement 中这一事实(其中,如果结果位模式被解释为无符号整数——这就是.un 的意思——0 具有最小的可能值),所以它会将x == 0 翻译成unchecked((uint)x) > 0。
事实证明编译器可以对null进行不等式检查:
static bool IsNotNull(object obj)
{
return obj != null;
}
编译器生成的 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 引用的位模式是任何对象引用可能的最小位模式。
Common Language Infrastructure Annotated Standard (1st edition from Oct 2003)(第 491 页,作为表 6-4 “二进制比较或分支操作”的脚注)明确提到了此快捷方式:
"cgt.un 在 ObjectRefs (O) 上是允许且可验证的。这通常在将 ObjectRef 与 null 进行比较时使用(没有“比较不等于”指令,否则会更明显解决方案)。”