2015-01-23 2 views
12

У меня есть этот код я бегу в 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.equals (х) правда. Таким образом, десятичное число считается равным длине с тем же значением, но long не считает себя равным десятичной, имеющей одно и то же значение.

В чем причина этого поведения?

Обновление:

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

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

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 
} 

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

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

+0

fyi, отбрасывая десятичную дробь на длинные работы: 'x.Equals ((long) y) .Dump();' – DLeh

+0

Хотя это не совсем объясняет это поведение, вы можете взглянуть на это: http://stackoverflow.com/questions/485175/is-it-safe-to-check-floating-point-values-for-equality-to-0-in-c-net/485210#485210 – seN

+0

@leppie : В реальной программе мы их не сравниваем. Мы обнаружили это, выполнив некоторую отладку, и мне показалось странным - отсюда и мой пост. – costa

ответ

18

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

y.Equals(x); 

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

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

x.Equals(y) 

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

+0

Этот ответ плюс ссылка из другого ответа - http://stackoverflow.com/a/28118709/477420 дает полное объяснение. –

+0

Этот ответ странный, но все же как-то чувствует себя правильно ... Вы говорите, что десятичный знак знает о длинном, но долго не знает о десятичной (с точки зрения преобразования)? – leppie

+0

Нет, он говорит, что 'decimal' не может быть преобразован в' long', не подвергая риску потери точности, и поэтому нет никакого неявного преобразования. Вы можете преобразовать 'long' в' decimal' без потери данных, и поэтому в этом направлении есть преобразование. – MarcinJuraszek

-2

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

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

+1

Они оба типа значений – MarcinJuraszek

+1

Если указатели не идентичны Object.Equals не удастся, так как объекты действительно не равны. –

+0

Object.equals здесь не называется – MarcinJuraszek

6

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

От MSDN:

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

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

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

Смежные вопросы