简短答案:
IL中没有“比较不等于”指令,因此C#!=
运算符没有确切的对应关系,因此不能按字面意义进行翻译。
但是,有一条“比较等价”指令(ceq
与==
运算符直接对应),因此在一般情况下,x != y
它的翻译时间稍长一些(x == y) == false
。
有也是一个“比较,大于”在IL(指令cgt
),它允许编译采取一定的快捷键(即产生较短的IL代码),一个是对空的对象,不平等的比较,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;
}
编译器产生的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进行比较时通常使用(不存在“ compare-not-equal”指令,否则将是更明显的解决方案)。
int
的范围有相同的表示int
,因为他们在做uint
。这比二的补码要弱得多。