2015-01-23 6 views
12

Ich habe diesen Code, den ich in LINQPad laufen:Warum ist der Wert zwischen lang und dezimal nicht kommutativ?

9223372036854775807 
    9223372036854775807 
    True 
    True 
    False 
    False 
    False 
    True 

Beachten Sie die letzten beiden Zeilen: x.Equals (y) ist falsch, sondern y.Equals (x

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(); 

Es diesen Ausgang erzeugt) ist wahr. So betrachtet sich das Dezimaltrennzeichen als gleich lang mit dem gleichen Wert, aber das lange betrachtet sich nicht als gleich dem Dezimalwert, der denselben Wert hat.

Was ist die Erklärung für dieses Verhalten?

Update:

Ich akzeptierte Lees Antwort.

war ich sehr neugierig auf diese und schrieb dieses kleine Programm:

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(); 
    } 
    } 
} 

die ich dann in IL zerlegt:

.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 
} 

Sie können in der Tat sehen, dass der Long-Wert in Dezimalzahlen umgewandelt wird.

Danke Jungs!

+0

fyi, wirft die Dezimalzahl zurück zu einem langen funktioniert: 'x.Equals ((lang) y) .Dump();' – DLeh

+0

Während dies nicht genau dieses Verhalten zu erklären, möchten Sie vielleicht einen Blick darauf werfen dies: 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 : Im echten Programm vergleichen wir sie nicht. Wir haben dies entdeckt, indem wir ein Debugging durchgeführt haben und ich fand es seltsam - daher mein Beitrag. – costa

Antwort

18

geschieht, weil in

y.Equals(x); 

die decimal.Equals(decimal) Überlastung genannt wird, da es eine implicit conversion zwischen long und decimal. Als Ergebnis wird der Vergleich wahr zurückgegeben.

Da es jedoch keine implizite Konvertierung decimal-long ist

x.Equals(y) 

Anrufe long.Equals(object) die y verpackt werden Ursachen und der Vergleich liefert false, da es nicht unboxed zu einem lang sein kann.

+0

Diese Antwort plus Link von anderer Antwort - http://stackoverflow.com/a/28118709/477420 gibt vollständige Erklärung. –

+0

Diese Antwort ist seltsam, aber irgendwie fühlt sich immer noch richtig ... Willst du sagen, während dezimal weiß über lange, aber lange nicht über Dezimal (in Bezug auf die Konvertierung) wissen? – leppie

+0

Nein, er sagt, 'decimal' kann nicht in' long' konvertiert werden, ohne einen Präzisionsverlust zu riskieren, und daher gibt es keine implizite Konvertierung. Sie können "long" in "decimal" ohne Datenverlust umwandeln und deshalb gibt es eine Konvertierung in diese Richtung. – MarcinJuraszek

-2

Sie vergleichen Objektreferenzen und Werte. Die Referenzen sind natürlich nicht die gleichen - abgesehen von der Referenz, die Sie explizit in Zeile 2 gesetzt haben. Die Werte sind jedoch.

C# kümmert sich automatisch um die Zeigerverwaltung (z. B. "Referenzieren von Speicher"). Sie greifen auf diese Ebene zu, die aus der Syntax nicht offensichtlich ist. Dies ist die Natur von C#.

+1

Sie sind beide Werttypen – MarcinJuraszek

+1

Wenn die Zeiger nicht identisch sind, wird Object.Equals fehlschlagen, da die Objekte tatsächlich nicht gleich sind. –

+0

Object.equals heißt hier nicht – MarcinJuraszek

6

Implizite vs explizite Konvertierungen.

Von MSDN:

Implizite Konvertierungen: keine spezielle Syntax erforderlich, da der Umwandlungstyp sicher ist und keine Daten verloren gehen. Beispiele hierfür sind Konvertierungen von kleineren in größere Integraltypen und Konvertierungen von abgeleiteten Klassen in Basisklassen.

Explizite Konvertierungen (Umwandlungen): Explizite Konvertierungen erfordern einen Umwandlungsoperator . Casting ist erforderlich, wenn Informationen in der -Konvertierung verloren gehen oder wenn die Konvertierung für andere Gründe möglicherweise nicht erfolgreich ist. Typische Beispiele sind die numerische Konvertierung in einen Typ mit geringerer Genauigkeit oder einem kleineren Bereich und die Konvertierung einer Basisklasse in eine abgeleitete Klasse.

Eine lange wird leicht in eine Dezimalzahl konvertieren, aber das Gegenteil ist nicht wahr, so dass die Auswertung fehlschlägt.Diese

Verwandte Themen