Почему Равны между длинными и десятичными не являются коммутативными?
У меня есть этот код, который я запускаю в 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. Однако значения имеют.
С# автоматически выполняет управление указателем (например, "Ссылка на память" ) для вас. Вы получаете доступ к этому слою, который изначально не является очевидным из синтаксиса. Это характер С#.