簡潔な答え:
IL には「compare-not-equal」命令がないため、C#!=
演算子には正確な対応がなく、文字通りに変換できません。
ただし、「compare-equal」命令 ( 、演算子ceq
への直接対応) があるため、一般的なケースでは、少し長い同等の のように変換されます。==
x != y
(x == y) == false
IL ( ) には「より大なり比較」命令もあり、cgt
これによりコンパイラは特定のショートカットを実行できます (つまり、より短い IL コードを生成できます) obj != null
。obj > null
"。
もう少し詳しく見てみましょう。
ILに「compare-not-equal」命令がない場合、次のメソッドはコンパイラによってどのように変換されますか?
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
}
コンパイラは、符号付き整数が2 の補数で格納されるという事実を利用できます(ここで、結果のビット パターンが符号なし整数として解釈される場合、つまり、0.un
が可能な限り最小の値を持ちます)。.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 (2003 年 10 月の第 1 版) (491 ページ、表 6-4「バイナリ比較または分岐操作」の脚注)で明示的に言及されています。
" cgt.un
ObjectRefs (O) で許可され、検証可能です。これは、ObjectRef を null と比較するときに一般的に使用されます ("compare-not-equal" 命令はありません。それ以外の場合は、より明白な解決策になります)。