Pour une raison quelconque, je me suis faufilé dans la source .NET Framework de la classe Double
et j'ai découvert que la déclaration de ==
est:
public static bool operator ==(Double left, Double right) {
return left == right;
}
La même logique s'applique à chaque opérateur.
- Quel est l'intérêt d'une telle définition?
- Comment ça marche?
- Pourquoi ne crée-t-il pas une récursion infinie?
c#
.net
language-lawyer
Thomas Ayoub
la source
la source
ceq
est plutôt publié en IL. Ceci est juste là pour remplir un but de documentation, mais je ne trouve pas la source.Réponses:
En réalité, le compilateur transformera l'
==
opérateur enceq
code IL et l'opérateur que vous mentionnez ne sera pas appelé.La raison pour laquelle l'opérateur dans le code source est probable est qu'il peut être appelé à partir de langages autres que C # qui ne le traduisent pas en un
CEQ
appel directement (ou par réflexion). Le code dans l'opérateur sera compilé en aCEQ
, il n'y a donc pas de récursivité infinie.En fait, si vous appelez l'opérateur par réflexion, vous pouvez voir que l'opérateur est appelé (plutôt qu'une
CEQ
instruction), et n'est évidemment pas infiniment récursif (puisque le programme se termine comme prévu):IL résultant (compilé par LinqPad 4):
Il est intéressant - les mêmes opérateurs n'existent pas ( que ce soit dans la source de référence ou par réflexion) pour les types entiers, seulement
Single
,Double
,Decimal
,String
etDateTime
qui réfute ma théorie selon laquelle ils existent pour appeler d'autres langues. Évidemment, vous pouvez assimiler deux entiers dans d'autres langues sans ces opérateurs, nous revenons donc à la question "pourquoi existent-ils pourdouble
"?la source
La principale confusion ici est que vous supposez que toutes les bibliothèques .NET (dans ce cas, la bibliothèque numérique étendue, qui ne fait pas partie de la BCL) sont écrites en C # standard. Ce n'est pas toujours le cas et différentes langues ont des règles différentes.
En C # standard, le morceau de code que vous voyez entraînerait un débordement de pile, en raison du fonctionnement de la résolution de surcharge des opérateurs. Cependant, le code n'est pas réellement en C # standard - il utilise essentiellement des fonctionnalités non documentées du compilateur C #. Au lieu d'appeler l'opérateur, il émet ce code:
Voilà :) Il n'y a pas de code C # 100% équivalent - ce n'est tout simplement pas possible en C # avec votre propre type.
Même dans ce cas, l'opérateur réel n'est pas utilisé lors de la compilation de code C # - le compilateur fait un tas d'optimisations, comme dans ce cas, où il remplace l'
op_Equality
appel par le simpleceq
. Encore une fois, vous ne pouvez pas répliquer cela dans votre propreDoubleEx
structure - c'est la magie du compilateur.Ce n'est certainement pas une situation unique dans .NET - il y a beaucoup de code qui n'est pas valide, C # standard. Les raisons sont généralement (a) des hacks de compilateur et (b) un langage différent, avec les hacks d'exécution impairs (c) (je vous regarde
Nullable
,!).Étant donné que le compilateur Roslyn C # est une source oepn, je peux en fait vous indiquer l'endroit où la résolution de surcharge est décidée:
L'endroit où tous les opérateurs binaires sont résolus
Les "raccourcis" pour les opérateurs intrinsèques
Lorsque vous regardez les raccourcis, vous verrez que l'égalité entre double et double se traduit par l'opérateur double intrinsèque, jamais par l'
==
opérateur réel défini sur le type. Le système de types .NET doit prétendre queDouble
c'est un type comme les autres, mais pas C # -double
est une primitive en C #.la source
#if
et d'autres artefacts qui ne seraient pas présents dans le code compilé. De plus, s'il a été rétro-conçu pour,double
pourquoi n'a-t-il pas été conçu pourint
oulong
? Je pense qu'il y a une raison pour le code source, mais je crois que l'utilisation de==
à l'intérieur de l'opérateur est compilée en unCEQ
qui empêche la récursivité. Puisque l'opérateur est un opérateur "prédéfini" pour ce type (et ne peut pas être remplacé), les règles de surcharge ne s'appliquent pas.double
ne fait pas partie de la BCL - c'est dans une bibliothèque séparée, qui se trouve juste être incluse dans la spécification C #. Oui, le==
est compilé en aceq
, mais cela signifie toujours qu'il s'agit d'un hack de compilateur que vous ne pouvez pas répliquer dans votre propre code, et quelque chose qui ne fait pas partie de la spécification C # (tout comme lefloat64
champ de laDouble
structure). Ce n'est pas une partie contractuelle de C #, il est donc inutile de le traiter comme un C # valide, même s'il a été compilé avec le compilateur C #.double
la même manière queint
etlong
-int
et celong
sont des types primitifs que tous les langages .NET doivent prendre en charge.float
,decimal
etdouble
ne le sont pas.La source des types primitifs peut prêter à confusion. Avez-vous vu la toute première ligne de la
Double
structure?Normalement, vous ne pouvez pas définir une structure récursive comme celle-ci:
Les types primitifs ont également leur support natif dans CIL. Normalement, ils ne sont pas traités comme des types orientés objet. Un double est juste une valeur 64 bits s'il est utilisé comme
float64
dans CIL. Cependant, s'il est traité comme un type .NET habituel, il contient une valeur réelle et il contient des méthodes comme tous les autres types.Donc ce que vous voyez ici est la même situation pour les opérateurs. Normalement, si vous utilisez directement le type double, il ne sera jamais appelé. BTW, sa source ressemble à ceci dans CIL:
Comme vous pouvez le voir, il n'y a pas de boucle sans fin (l'
ceq
instrument est utilisé au lieu d'appeler leSystem.Double::op_Equality
). Ainsi, lorsqu'un double est traité comme un objet, la méthode opérateur sera appelée, qui finira par le traiter comme lefloat64
type primitif au niveau CIL.la source
public struct MyNumber { internal MyNumber m_value; }
. Il ne peut pas être compilé, bien sûr. L'erreur est l' erreur CS0523: Le membre de structure 'MyNumber.m_value' de type 'MyNumber' provoque un cycle dans la mise en page de structureJ'ai jeté un œil au CIL avec JustDecompile. L'intérieur
==
est traduit en code d' opération CIL ceq . En d'autres termes, c'est l'égalité CLR primitive.J'étais curieux de voir si le compilateur C # ferait référence
ceq
ou l'==
opérateur lors de la comparaison de deux valeurs doubles. Dans l'exemple trivial que j'ai trouvé (ci-dessous), il a utiliséceq
.Ce programme:
génère le CIL suivant (notez l'instruction avec l'étiquette
IL_0017
):la source
Comme indiqué dans la documentation Microsoft pour l'espace de noms System.Runtime.Versioning: les types trouvés dans cet espace de noms sont destinés à être utilisés dans le .NET Framework et non pour les applications utilisateur. L'espace de noms System.Runtime.Versioning contient des types avancés qui prennent en charge la gestion des versions dans implémentations côte à côte du .NET Framework.
la source
System.Runtime.Versioning
à voir avecSystem.Double
?