Почему компилятор 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", которая в противном случае было бы более очевидным решением)."