Короткий ответ:
В IL нет инструкции «сравнить не равно», поэтому оператор C# !=
не имеет точного соответствия и не может быть переведен буквально.
Однако существует инструкция сравнения-равно (ceq
, прямое соответствие оператору ==
), поэтому в общем случае x != y
переводится как его немного более длинный эквивалент (x == y) == false
.
Также в IL есть инструкция сравнения с большим, чем (cgt
), которая позволяет компилятору использовать определенные сокращения (например, генерировать более короткий код IL). получить перевод, как если бы они были 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
является наименьшей битовой комбинацией, возможной для любой ссылки на объект.
Этот ярлык явно упоминается на Common Аннотированный стандарт языковой инфраструктуры (1-е издание от октября 2003 г.) (на странице 491, в качестве сноски к Таблице 6-4, Двоичные сравнения или операции ветвления):
cgt.un
разрешено и может быть проверено в ObjectRefs (O). Это обычно используется при сравнении ObjectRef с нулевым значением (отсутствует инструкция сравнения неравно, которая в противном случае была бы более очевидным решением).
person
stakx - no longer contributing
schedule
28.02.2015