Почему компилятор C# переводит это!= сравнение, как если бы это было > сравнение?
я совершенно случайно обнаружил, что компилятор C# поворачивает этот метод:
static bool IsNotNull(object obj)
{
return obj != null;
}
...в это CIL:
.method private hidebysig static bool IsNotNull(object obj) cil managed
{
ldarg.0 // obj
ldnull
cgt.un
ret
}
...или, если вы предпочитаете смотреть на декомпилированный код C#:
static bool IsNotNull(object obj)
{
return obj > null; // (note: this is not a valid C# expression)
}
как получилось, что !=
переводится как ">
"?
1 ответ:
короткий ответ:
в IL нет инструкции "compare-not-equal", поэтому C#
!=
оператор не имеет точного соответствия и не могут быть переведены буквально.однако существует инструкция" compare-equal" (
ceq
, прямая корреспонденция с==
оператор), так что в общем случае,x != y
переводится как его немного более длинный эквивалент(x == y) == false
.здесь и инструкция "сравнить-больше-чем" в ил (
cgt
), который позволяет компилятору принимать определенные ярлыки (т. е. генерировать более короткий код IL), одним из которых является сравнение неравенства объектов с null,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 }
компилятор может воспользоваться тем, что целые числа хранятся в два (где, если результирующие битовые шаблоны интерпретируется как целые числа без знака-вот что такое
.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 (1-е издание от октября 2003 года) (на стр. 491, как сноска таблицы 6-4, "Бинарные сравнения или операции ветвления"):
"
cgt.un
допускается и проверяется на ObjectRefs (O). Это обычно используется при сравнении ObjectRef с null (нет инструкции "compare-not-equal", которая в противном случае было бы более очевидным решением)."