Ответ 1
Ошибочная интерпретация рефлектора
Декомпиляция, которую вы видите из Reflector, на самом деле является ошибкой в Reflector. Отражатель должен иметь возможность декомпилировать функцию, в которой сравниваются два удвоения; в этих функциях вы найдете ceq
, испускаемый прямо в код. В результате Reflector интерпретирует инструкцию ceq
как == между двумя удвоениями, чтобы помочь декомпилировать функцию, в которой сравниваются два удвоения.
По умолчанию типы значений не связаны с реализацией ==. (Не определяемые пользователем структуры наследуют перегруженный == оператор?) Однако все встроенные скалярные типы имеют явно перегруженный оператор, что компилятор переводит в соответствующий CIL. Перегрузка также содержит простое сравнение ceq
, так что вызовы с динамической/поздней привязкой/рефлексией при перегрузке оператора == не будут терпеть неудачу.
Подробнее
Для предопределенных типов значений оператор равенства (==) возвращает true, если значения его операндов равны, в противном случае - false. Для справки типы, отличные от string, == возвращает true, если его два операнда ссылаются на тот же объект. Для типа строки == сравнивает значения строки.
- http://msdn.microsoft.com/en-us/library/53k8ybth.aspx
То, что вы сказали, подразумевает, что == использует семантику ссылочного типа для сравнения double
. Однако, поскольку double
- тип значения, он использует семантику значений. Вот почему 3 == 3
истинно, даже если это разные объекты стека.
Вы можете почти думать об этом преобразовании компилятора как о том, как объект LINQ Queryable содержит методы расширения с кодом в них, но компилятор переводит эти вызовы в деревья выражений, которые затем передаются поставщику LINQ. В обоих случаях основная функция никогда не вызвана.
Двойная сравнительная семантика
Документация для Double ссылается на то, как работает инструкция ceq
CIL:
Если два значения Double.NaN проверяются на равенство, вызывая метод Equals, метод возвращает true. Однако, если два значения NaN проверяются на равенство с помощью оператора равенства, оператор возвращает false. Если вы хотите определить, является ли значение Double не числом (NaN), альтернативой является вызов метода IsNaN.
- http://msdn.microsoft.com/en-us/library/ya2zha7s.aspx
Исходный источник компилятора
Если вы посмотрите в декомпилированном источнике компилятора С#, вы найдете следующий код для обработки прямого перевода двойных сравнений в ceq
:
private void EmitBinaryCondOperator(BoundBinaryOperator binOp, bool sense)
{
int num;
ConstantValue constantValue;
bool flag = sense;
BinaryOperatorKind kind = binOp.OperatorKind.OperatorWithLogical();
if (kind <= BinaryOperatorKind.GreaterThanOrEqual)
{
switch (kind)
{
...
case BinaryOperatorKind.Equal:
goto Label_0127;
...
}
}
...
Label_0127:
constantValue = binOp.Left.ConstantValue;
if (((constantValue != null) && constantValue.IsPrimitiveZeroOrNull) && !constantValue.IsFloating)
{
...
return;
}
constantValue = binOp.Right.ConstantValue;
if (((constantValue != null) && constantValue.IsPrimitiveZeroOrNull) && !constantValue.IsFloating)
{
...
return;
}
this.EmitBinaryCondOperatorHelper(ILOpCode.Ceq, binOp.Left, binOp.Right, sense);
return;
}
Вышеприведенный код от Roslyn.Compilers.CSharp.CodeGen.CodeGenerator.EmitBinaryCondOperator(...)
, и я добавил "...", чтобы сделать код более читаемым для этой цели.