Коротка відповідь:
В 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;
}
Отриманий ІЛ дещо коротший, ніж у загальному випадку:
.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;
}
Компілятор видає майже той самий ІЛ, що і для IsNotZero
:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0
ldnull // (note: this is the only difference)
cgt.un
ret
}
Мабуть, компілятору дозволено припустити, що бітова модель null
посилання є найменшим шаблоном бітів, можливим для будь-якого посилання на об'єкт.
Цей ярлик явно згадується в Анотованому стандарті загальної мовної інфраструктури (1-е видання від жовтня 2003 р.) (На стор. 491, як виноска до таблиці 6-4, "Бінарні порівняння або операції з філіями"):
" cgt.un
дозволено і перевірено в ObjectRefs (O). Це зазвичай використовується при порівнянні ObjectRef з null (немає інструкції" порівняти-не-рівно ", яка в іншому випадку була б більш очевидним рішенням."
int
Має значення лише те, що підписані цілі числа зберігаються таким чином, що негативні значення в діапазоні 's мають таке ж представлення,int
як і вuint
. Це набагато слабша вимога, ніж доповнення двох.