Почему Равны между длинными и десятичными не являются коммутативными?

У меня есть этот код, который я запускаю в linqpad:

    long x = long.MaxValue;
    decimal y = x;

    x.Dump();
    y.Dump();

    (x == y).Dump();
    (y == x).Dump();

    Object.Equals(x, y).Dump();
    Object.Equals(y, x).Dump();
    x.Equals(y).Dump();
    y.Equals(x).Dump();

Он производит этот вывод:

    9223372036854775807
    9223372036854775807
    True
    True
    False
    False
    False
    True

Обратите внимание на последние две строки: x.Equals(y) - false, но y.Equals(x) - true. Таким образом, десятичное число считается равным длине с тем же значением, но long не считает себя равным десятичной, имеющей одно и то же значение.

Какое объяснение этого поведения?

Update:

Я принял ответ Ли.

Мне было очень интересно об этом и написала эту небольшую программу:

using System;
namespace TestConversion
{
  class Program
  {
    static void Main(string[] args)
    {
      long x = long.MaxValue;
      decimal y = x;

      Console.WriteLine(x);
      Console.WriteLine(y);

      Console.WriteLine(x == y);
      Console.WriteLine(y == x);

      Console.WriteLine(Object.Equals(x, y));
      Console.WriteLine(Object.Equals(y, x));
      Console.WriteLine(x.Equals(y));
      Console.WriteLine(y.Equals(x));
      Console.ReadKey();
    }
  }
}

Что я тогда разобрал в IL:

.method private hidebysig static void Main(string[] args) cil managed
{
  .entrypoint
  .maxstack 2
  .locals init (
    [0] int64 x,
    [1] valuetype [mscorlib]System.Decimal y)
  L_0000: nop 
  L_0001: ldc.i8 9223372036854775807
  L_000a: stloc.0 
  L_000b: ldloc.0 
  L_000c: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int64)
  L_0011: stloc.1 
  L_0012: ldloc.0 
  L_0013: call void [mscorlib]System.Console::WriteLine(int64)
  L_0018: nop 
  L_0019: ldloc.1 
  L_001a: call void [mscorlib]System.Console::WriteLine(valuetype [mscorlib]System.Decimal)
  L_001f: nop 
  L_0020: ldloc.0 
  L_0021: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int64)
  L_0026: ldloc.1 
  L_0027: call bool [mscorlib]System.Decimal::op_Equality(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
  L_002c: call void [mscorlib]System.Console::WriteLine(bool)
  L_0031: nop 
  L_0032: ldloc.1 
  L_0033: ldloc.0 
  L_0034: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int64)
  L_0039: call bool [mscorlib]System.Decimal::op_Equality(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
  L_003e: call void [mscorlib]System.Console::WriteLine(bool)
  L_0043: nop 
  L_0044: ldloc.0 
  L_0045: box int64
  L_004a: ldloc.1 
  L_004b: box [mscorlib]System.Decimal
  L_0050: call bool [mscorlib]System.Object::Equals(object, object)
  L_0055: call void [mscorlib]System.Console::WriteLine(bool)
  L_005a: nop 
  L_005b: ldloc.1 
  L_005c: box [mscorlib]System.Decimal
  L_0061: ldloc.0 
  L_0062: box int64
  L_0067: call bool [mscorlib]System.Object::Equals(object, object)
  L_006c: call void [mscorlib]System.Console::WriteLine(bool)
  L_0071: nop 
  L_0072: ldloca.s x
  L_0074: ldloc.1 
  L_0075: box [mscorlib]System.Decimal
  L_007a: call instance bool [mscorlib]System.Int64::Equals(object)
  L_007f: call void [mscorlib]System.Console::WriteLine(bool)
  L_0084: nop 
  L_0085: ldloca.s y
  L_0087: ldloc.0 
  L_0088: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int64)
  L_008d: call instance bool [mscorlib]System.Decimal::Equals(valuetype [mscorlib]System.Decimal)
  L_0092: call void [mscorlib]System.Console::WriteLine(bool)
  L_0097: nop 
  L_0098: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  L_009d: pop 
  L_009e: ret 
}

Вы действительно видите, что длинное значение преобразуется в десятичное.

Спасибо, ребята!

Ответ 1

Это происходит потому, что в

y.Equals(x);

перегрузка decimal.Equals(decimal) вызывается, поскольку существует неявное преобразование между long и decimal. В результате сравнение возвращает true.

Однако, поскольку неявное преобразование из decimal в long

x.Equals(y)

вызывает long.Equals(object), который вызывает коробку y, и сравнение возвращает false, поскольку оно не может быть опущено до длинного.

Ответ 2

Неявные vs Явные преобразования.

Из MSDN:

Неявные преобразования: специальный синтаксис не требуется, поскольку преобразование является безопасным по типу и данные не будут потеряны. Примеры включают конверсии от меньших до более крупных интегральных типов и конверсии от производных классов до базовых классов.

Явные преобразования (отливки): Явные преобразования требуют приведения оператор. Кастинг необходим, если информация может быть потеряна в преобразование, или когда преобразование может не удаться для других причины. Типичные примеры включают числовое преобразование в тип, который имеет меньшую точность или меньший диапазон, а преобразование базового класса instance к производному классу.

Длинное преобразование легко преобразуется в десятичное число, но обратное неверно, поэтому оценка не выполняется.

Ответ 3

Вы сравниваете ссылки и значения объектов. Конечно, ссылки не совпадают - кроме ссылки, которую вы явно задали в строке 2. Однако значения имеют.

С# автоматически выполняет управление указателем (например, "Ссылка на память" ) для вас. Вы получаете доступ к этому слою, который изначально не является очевидным из синтаксиса. Это характер С#.