Aus irgendeinem Grund habe ich mich in die .NET Framework-Quelle für die Klasse geschlichen Double
und festgestellt, dass die Deklaration von ==
:
public static bool operator ==(Double left, Double right) {
return left == right;
}
Die gleiche Logik gilt für jeden Bediener.
- Was ist der Sinn einer solchen Definition?
- Wie funktioniert es?
- Warum erzeugt es keine unendliche Rekursion?
c#
.net
language-lawyer
Thomas Ayoub
quelle
quelle
ceq
in IL ausgegeben wird. Dies ist nur dazu da, um einen Dokumentationszweck zu erfüllen. Die Quelle kann jedoch nicht gefunden werden.Antworten:
In Wirklichkeit verwandelt der Compiler den
==
Operator in einenceq
IL-Code, und der von Ihnen erwähnte Operator wird nicht aufgerufen.Der Grund für den Operator im Quellcode ist wahrscheinlich, dass er aus anderen Sprachen als C # aufgerufen werden kann, die ihn nicht
CEQ
direkt (oder durch Reflexion) in einen Aufruf übersetzen . Der Code innerhalb des Operators wird zu a kompiliertCEQ
, sodass es keine unendliche Rekursion gibt.Wenn Sie den Operator über Reflection aufrufen, können Sie tatsächlich sehen, dass der Operator aufgerufen wird (und nicht eine
CEQ
Anweisung) und offensichtlich nicht unendlich rekursiv ist (da das Programm wie erwartet beendet wird):Resultierende IL (zusammengestellt von LinqPad 4):
Interessanterweise - die gleichen Betreiber nicht existieren (entweder in der Referenzquelle oder über Reflexion) für integrale Typen, nur
Single
,Double
,Decimal
,String
, undDateTime
, die widerlegt meine Theorie , dass sie aus anderen Sprachen aufgerufen werden , existieren. Natürlich können Sie ohne diese Operatoren zwei Ganzzahlen in anderen Sprachen gleichsetzen. Wir kehren also zu der Frage zurück, warum sie existierendouble
.quelle
Die Hauptverwirrung besteht darin, dass Sie davon ausgehen, dass alle .NET-Bibliotheken (in diesem Fall die Extended Numerics Library, die nicht Teil der BCL ist) in Standard-C # geschrieben sind. Dies ist nicht immer der Fall und verschiedene Sprachen haben unterschiedliche Regeln.
In Standard-C # würde der angezeigte Code aufgrund der Funktionsweise der Operatorüberlastungsauflösung zu einem Stapelüberlauf führen. Der Code befindet sich jedoch nicht in Standard-C # - er verwendet grundsätzlich undokumentierte Funktionen des C # -Compilers. Anstatt den Operator aufzurufen, wird der folgende Code ausgegeben:
Das war's :) Es gibt keinen 100% äquivalenten C # -Code - dies ist in C # mit Ihrem eigenen Typ einfach nicht möglich .
Selbst dann wird der eigentliche Operator beim Kompilieren von C # -Code nicht verwendet - der Compiler führt eine Reihe von Optimierungen durch, wie in diesem Fall, bei denen der
op_Equality
Aufruf nur durch den einfachen ersetzt wirdceq
. Auch hier können Sie dies nicht in Ihrer eigenenDoubleEx
Struktur replizieren - es ist Compiler-Magie.Dies ist sicherlich keine einzigartige Situation in .NET - es gibt viele ungültige Codes, Standard-C #. Die Gründe sind normalerweise (a) Compiler-Hacks und (b) eine andere Sprache mit den ungeraden (c) Laufzeit-Hacks (ich sehe dich an
Nullable
!).Da der Roslyn C # -Compiler eine Oepn-Quelle ist, kann ich Sie tatsächlich auf den Ort verweisen, an dem die Überlastungsauflösung entschieden wird:
Der Ort, an dem alle Binäroperatoren aufgelöst werden
Die "Verknüpfungen" für intrinsische Operatoren
Wenn Sie sich die Verknüpfungen ansehen, werden Sie feststellen, dass die Gleichheit zwischen double und double zu dem intrinsischen double-Operator führt, niemals zu dem tatsächlichen
==
Operator, der für den Typ definiert ist. Das .NET-Typsystem muss so tun, als wäreDouble
es ein Typ wie jeder andere, aber C # nicht -double
ist ein Grundelement in C #.quelle
#if
und andere Artefakte, die im kompilierten Code nicht vorhanden wären. Und wenn es rückentwickelt wurde,double
warum wurde es dann nicht rückentwickeltint
oderlong
? Ich denke, es gibt einen Grund für den Quellcode, aber ich glaube, dass die Verwendung von==
innerhalb des Operators zu einem kompiliert wird,CEQ
der eine Rekursion verhindert. Da der Operator ein "vordefinierter" Operator für diesen Typ ist (und nicht überschrieben werden kann), gelten die Überladungsregeln nicht.double
ist es nicht Teil der BCL - es befindet sich in einer separaten Bibliothek, die zufällig in der C # -Spezifikation enthalten ist. Ja, das==
wird zu a kompiliertceq
, aber das bedeutet immer noch, dass dies ein Compiler-Hack ist, den Sie nicht in Ihrem eigenen Code replizieren können, und etwas, das nicht Teil der C # -Spezifikation ist (genau wie dasfloat64
Feld in derDouble
Struktur). Es ist kein vertraglicher Bestandteil von C #, daher macht es wenig Sinn, es als gültiges C # zu behandeln, selbst wenn es mit dem C # -Compiler kompiliert wurde.double
wieint
undlong
-int
undlong
sind primitive Typen, die alle .NET-Sprachen unterstützen müssen.float
,decimal
Unddouble
sind es nicht.Die Quelle der primitiven Typen kann verwirrend sein. Hast du die allererste Zeile der
Double
Struktur gesehen?Normalerweise können Sie eine rekursive Struktur wie folgt nicht definieren:
Primitive Typen haben ihre native Unterstützung auch in CIL. Normalerweise werden sie nicht wie objektorientierte Typen behandelt. Ein Double ist nur ein 64-Bit-Wert, wenn es wie
float64
in CIL verwendet wird. Wenn es jedoch als normaler .NET-Typ behandelt wird, enthält es einen tatsächlichen Wert und Methoden wie alle anderen Typen.Was Sie hier sehen, ist die gleiche Situation für die Bediener. Wenn Sie den Typ mit doppeltem Typ direkt verwenden, wird er normalerweise nie aufgerufen. Übrigens sieht die Quelle in CIL folgendermaßen aus:
Wie Sie sehen können, gibt es keine Endlosschleife (das
ceq
Instrument wird verwendet, anstatt das aufzurufenSystem.Double::op_Equality
). Wenn also ein Double wie ein Objekt behandelt wird, wird die Operatormethode aufgerufen, die es schließlich alsfloat64
primitiven Typ auf CIL-Ebene behandelt.quelle
public struct MyNumber { internal MyNumber m_value; }
. Es kann natürlich nicht kompiliert werden. Der Fehler ist Fehler CS0523: Strukturelement 'MyNumber.m_value' vom Typ 'MyNumber' verursacht einen Zyklus im StrukturlayoutIch habe mir die CIL mit JustDecompile angesehen. Das Innere
==
wird in den CIL- Ceq- Op-Code übersetzt. Mit anderen Worten, es ist primitive CLR-Gleichheit.Ich war gespannt, ob der C # -Compiler darauf verweisen würde
ceq
==
beim Vergleich zweier Doppelwerte auf den Operator . In dem trivialen Beispiel, das ich mir (unten) ausgedacht habe, wurde es verwendetceq
.Dieses Programm:
generiert die folgende CIL (beachten Sie die Anweisung mit dem Label
IL_0017
):quelle
Wie in der Microsoft-Dokumentation für den System.Runtime.Versioning-Namespace angegeben: Die in diesem Namespace gefundenen Typen sind für die Verwendung in .NET Framework und nicht für Benutzeranwendungen vorgesehen. Der System.Runtime.Versioning-Namespace enthält erweiterte Typen, die die Versionierung unterstützen Side-by-Side-Implementierungen von .NET Framework.
quelle
System.Runtime.Versioning
zu tunSystem.Double
?