Основная путаница здесь заключается в том, что вы предполагаете, что все библиотеки .NET (в данном случае Библиотека расширенных числовых данных, которая не является частью BCL) написаны на стандартном C #. Это не всегда так, и на разных языках действуют разные правила.
В стандартном C # фрагмент кода, который вы видите, приведет к переполнению стека из-за того, как работает разрешение перегрузки оператора. Однако код на самом деле не в стандартном C # - он в основном использует недокументированные функции компилятора C #. Вместо вызова оператора он выдает такой код:
ldarg.0
ldarg.1
ceq
ret
Вот и все :) Не существует 100% эквивалентного кода C # - это просто невозможно в C # с вашим собственным типом.
Даже в этом случае фактический оператор не используется при компиляции кода C # - компилятор выполняет несколько оптимизаций, как в этом случае, когда он заменяет op_Equality
вызов простым ceq
. Опять же, вы не можете воспроизвести это в своей собственной DoubleEx
структуре - это магия компилятора.
Это, конечно, не уникальная ситуация в .NET - там много кода, который не соответствует стандарту C #. Причинами обычно являются (а) взлом компилятора и (б) другой язык, а также странные (в) взломы времени выполнения (я смотрю на вас Nullable
!).
Поскольку компилятор Roslyn C # является исходным кодом oepn, я могу указать вам место, где решается разрешение перегрузки:
Место, где разрешены все бинарные операторы
"Ярлыки" для внутренних операторов
Когда вы посмотрите на ярлыки, вы увидите, что равенство между double и double приводит к внутреннему оператору double, а не к фактическому ==
оператору, определенному для типа. Система типов .NET должна делать вид, что Double
это такой же тип, как и любой другой, но C # этого не делает - double
это примитив в C #.