Определение оператора "= = " для Double
по какой-то причине я пробирался в источник .NET Framework для класса Double и выяснил, что декларация == - это:
public static bool operator ==(Double left, Double right) {
return left == right;
}
та же логика применима к оператора.
- в чем смысл такого определения?
- как это работает?
- почему он не создает бесконечную рекурсию?
5 ответов:
на самом деле компилятор превратит
==оператор вceqкод IL, и оператор, который вы упомянули, не будет вызван.причина для оператора в исходном коде, вероятно, поэтому он может быть вызван из языков, отличных от C# , которые не переводят его в
CEQвызов непосредственно (или через отражение). Код внутри оператор будет быть скомпилированы вCEQ, так что нет бесконечного рекурсия.на самом деле, если вы вызываете оператор через отражение, вы можете видеть, что оператор вызывается (а не
CEQинструкция), и, очевидно, не является бесконечно рекурсивным (так как программа завершается, как ожидалось):double d1 = 1.1; double d2 = 2.2; MethodInfo mi = typeof(Double).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public ); bool b = (bool)(mi.Invoke(null, new object[] {d1,d2}));результирующий IL (скомпилированный LinqPad 4):
IL_0000: nop IL_0001: ldc.r8 9A 99 99 99 99 99 F1 3F IL_000A: stloc.0 // d1 IL_000B: ldc.r8 9A 99 99 99 99 99 01 40 IL_0014: stloc.1 // d2 IL_0015: ldtoken System.Double IL_001A: call System.Type.GetTypeFromHandle IL_001F: ldstr "op_Equality" IL_0024: ldc.i4.s 18 IL_0026: call System.Type.GetMethod IL_002B: stloc.2 // mi IL_002C: ldloc.2 // mi IL_002D: ldnull IL_002E: ldc.i4.2 IL_002F: newarr System.Object IL_0034: stloc.s 04 // CS00 IL_0036: ldloc.s 04 // CS00 IL_0038: ldc.i4.0 IL_0039: ldloc.0 // d1 IL_003A: box System.Double IL_003F: stelem.ref IL_0040: ldloc.s 04 // CS00 IL_0042: ldc.i4.1 IL_0043: ldloc.1 // d2 IL_0044: box System.Double IL_0049: stelem.ref IL_004A: ldloc.s 04 // CS00 IL_004C: callvirt System.Reflection.MethodBase.Invoke IL_0051: unbox.any System.Boolean IL_0056: stloc.3 // b IL_0057: retинтересно-одни и те же операторы не существуют (ни в ссылочном источнике, ни через отражение) для целых типов, только
Single,Double,Decimal,StringиDateTime, что опровергает мою теорию о том, что они существуют, чтобы называться из других языков. Очевидно, что вы можете приравнять два целых числа в других языках без этих операторов, поэтому мы возвращаемся к вопросу "почему они существуют дляdouble"?
основная путаница здесь заключается в том, что вы предполагаете, что все библиотеки .NET (в данном случае Расширенная библиотека Numerics, которая не часть BCL) написаны на стандартном C#. Это не всегда так, и разные языки имеют разные правила.
в стандартном C# фрагмент кода, который вы видите, приведет к переполнению стека из-за того, как работает разрешение перегрузки оператора. Однако код на самом деле не находится в стандартном C# - он в основном использует недокументированные функции компилятора C#. Вместо вызова оператора он выдает следующий код:
ldarg.0 ldarg.1 ceq retвот и все :) нет 100% эквивалентного кода C# - это просто невозможно в C# с свой тип.
даже то, собственно оператор не используется при компиляции кода C# - компилятор делает кучу оптимизаций, как в этом случае, где он заменяет
op_Equalityвызов с помощью простогоceq. Опять же, вы не можете повторить это в вашем собственныйDoubleExstruct-это магия компилятора.это, конечно, не уникальная ситуация в .NET - есть много кода, который не является допустимым, стандартный C#. Причины обычно (а) хаки компилятора и (б) другой язык, с нечетными (с) хаки времени выполнения (я смотрю на вас,
Nullable!).поскольку компилятор Roslyn C# является источником oepn, я могу указать вам место, где решается разрешение перегрузки:
место, где все бинарные операторы разрешены
"ярлыки" для встроенных операторов
когда вы посмотрите на ярлыки, вы увидите, что равенство между double и double приводит к встроенному оператору double,никогда в фактических
==оператор определенного типа. Система типов .NET должна притворяться, чтоDoubleэто тип, как и любой другой, но C# не -doubleявляется примитивом в C#.
источник примитивных типов может быть запутанным. Вы видели самую первую строку
Doubleструктура?обычно вы не можете определить рекурсивную структуру следующим образом:
public struct Double : IComparable, IFormattable, IConvertible , IComparable<Double>, IEquatable<Double> { internal double m_value; // Self-recursion with endless loop? // ... }примитивные типы также имеют свою собственную поддержку в CIL. Обычно они не рассматриваются как объектно-ориентированные типы. Double - это всего лишь 64-разрядное значение, если оно используется как
float64в CIL. Однако если он обрабатывается как обычный тип .NET, он содержит фактическое значение и содержит методы, как и любые другие виды.так что вы видите здесь такую же ситуацию для операторов. Обычно, если вы используете тип double type напрямую, он никогда не будет вызван. Кстати, его источник выглядит так в CIL:
.method public hidebysig specialname static bool op_Equality(float64 left, float64 right) cil managed { .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() .custom instance void __DynamicallyInvokableAttribute::.ctor() .maxstack 8 L_0000: ldarg.0 L_0001: ldarg.1 L_0002: ceq L_0004: ret }как вы можете видеть, нет бесконечного цикла (
ceqинструмент используется вместо вызоваSystem.Double::op_Equality). Поэтому, когда double обрабатывается как объект, будет вызван метод оператора, который в конечном итоге обработает его какfloat64примитивный тип на уровне CIL.
Я взглянул на CIL С JustDecompile. Внутренний
==переводится в CIL ceq op код. Другими словами, это примитивное равенство CLR.мне было любопытно посмотреть, будет ли компилятор C# ссылаться
ceqили==оператор при сравнении двух значений Double. В тривиальном примере, который я придумал (ниже), он использовалceq.программы:
void Main() { double x = 1; double y = 2; if (x == y) Console.WriteLine("Something bad happened!"); else Console.WriteLine("All is right with the world"); }генерирует следующий CIL (обратите внимание заявление с меткой
IL_0017):IL_0000: nop IL_0001: ldc.r8 00 00 00 00 00 00 F0 3F IL_000A: stloc.0 // x IL_000B: ldc.r8 00 00 00 00 00 00 00 40 IL_0014: stloc.1 // y IL_0015: ldloc.0 // x IL_0016: ldloc.1 // y IL_0017: ceq IL_0019: stloc.2 IL_001A: ldloc.2 IL_001B: brfalse.s IL_002A IL_001D: ldstr "Something bad happened!" IL_0022: call System.Console.WriteLine IL_0027: nop IL_0028: br.s IL_0035 IL_002A: ldstr "All is right with the world" IL_002F: call System.Console.WriteLine IL_0034: nop IL_0035: ret
Как указано в документации Microsoft для системы.Во время выполнения.Пространство имен управления версиями: типы, найденные в этом пространстве имен, предназначены для использования в .NET Framework, а не для пользовательских приложений.система.Во время выполнения.Пространство имен управления версиями содержит расширенные типы, которые поддерживают управление версиями в параллельных реализациях платформы .NET Framework.
Comments