Selon la documentation de l' ==
opérateur dans MSDN ,
Pour les types de valeurs prédéfinis, l'opérateur d'égalité (==) renvoie true si les valeurs de ses opérandes sont égales, false sinon. Pour les types de référence autres que chaîne, == renvoie true si ses deux opérandes font référence au même objet. Pour le type de chaîne, == compare les valeurs des chaînes. Les types de valeurs définis par l'utilisateur peuvent surcharger l'opérateur == (voir opérateur). Il en va de même pour les types de référence définis par l'utilisateur, bien que par défaut == se comporte comme décrit ci-dessus pour les types de référence prédéfinis et définis par l'utilisateur.
Alors pourquoi cet extrait de code ne parvient-il pas à être compilé?
bool Compare<T>(T x, T y) { return x == y; }
J'obtiens l'erreur Operator '==' ne peut pas être appliqué aux opérandes de type 'T' et 'T' . Je me demande pourquoi, dans la mesure où je comprends l' ==
opérateur est prédéfini pour tous les types?
Edit: Merci à tous. Je n'ai pas remarqué au début que la déclaration concernait uniquement les types de référence. J'ai également pensé que la comparaison bit par bit est fournie pour tous les types de valeur, ce que je sais maintenant n'est pas correct.
Mais, dans le cas où j'utilise un type de référence, l' ==
opérateur utiliserait-il la comparaison de référence prédéfinie, ou utiliserait-il la version surchargée de l'opérateur si un type en définissait un?
Edit 2: Par essais et erreurs, nous avons appris que l' ==
opérateur utilisera la comparaison de référence prédéfinie lors de l'utilisation d'un type générique sans restriction. En fait, le compilateur utilisera la meilleure méthode qu'il puisse trouver pour l'argument de type restreint, mais ne cherchera pas plus loin. Par exemple, le code ci-dessous sera toujours imprimé true
, même lorsqu'il Test.test<B>(new B(), new B())
est appelé:
class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
la source
==
n'est pas autorisé entre deux opérandes du même type. Cela est vrai pour lesstruct
types (sauf les types "prédéfinis") qui ne surchargent pas leoperator ==
. Comme exemple simple, essayez ceci:var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, alors vous ne pouvez pas vérifierkvp1 == kvp2
carKeyValuePair<,>
est une structure, ce n'est pas un type prédéfini C # et il ne surcharge pas leoperator ==
. Pourtant, un exemple est donné parvar li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
lequel vous ne pouvez pas fairee1 == e2
(ici nous avons la structure imbriquéeList<>.Enumerator
(appelée"List`1+Enumerator[T]"
par le runtime) qui ne surcharge pas==
).bool
d'unvoid
...Réponses:
"... par défaut == se comporte comme décrit ci-dessus pour les types de référence prédéfinis et définis par l'utilisateur."
Le type T n'est pas nécessairement un type de référence, le compilateur ne peut donc pas faire cette hypothèse.
Cependant, cela se compilera car il est plus explicite:
Suivi d'une question supplémentaire: "Mais, si j'utilise un type de référence, l'opérateur == utiliserait-il la comparaison de référence prédéfinie, ou utiliserait-il la version surchargée de l'opérateur si un type en définissait un?"
J'aurais pensé que == sur les génériques utiliserait la version surchargée, mais le test suivant démontre le contraire. Intéressant ... j'aimerais savoir pourquoi! Si quelqu'un sait, veuillez partager.
Production
Inline: surchargé == appelé
Générique:
Appuyez sur n'importe quelle touche pour continuer . . .
Suivi 2
Je tiens à souligner que changer ma méthode de comparaison en
provoque l'appel de l'opérateur == surchargé. Je suppose que sans spécifier le type (comme où ), le compilateur ne peut pas déduire qu'il devrait utiliser l'opérateur surchargé ... bien que je pense qu'il aurait suffisamment d'informations pour prendre cette décision même sans spécifier le type.
la source
Equals
méthode surchargée (pas dans l'==
opérateur).==
entre les types génériquesT
etT
, la meilleure surcharge est trouvée, étant donné les contraintes qui sont portées parT
(il y a une règle spéciale qui ne mettra jamais un type de valeur pour cela (ce qui donnerait un résultat sans signification), donc il doit y avoir une contrainte garantissant qu'il s'agit d'un type de référence). Dans votre suivi 2 , si vous venez avec desDerivedTest
objets, etDerivedTest
dérive deTest
mais introduit une nouvelle surcharge de==
, vous aurez à nouveau le "problème". Quelle surcharge est appelée, est "gravée" dans l'IL au moment de la compilation.Comme d'autres l'ont dit, cela ne fonctionnera que lorsque T est contraint d'être un type de référence. Sans aucune contrainte, vous pouvez comparer avec null, mais seulement null - et cette comparaison sera toujours false pour les types de valeur non nullable.
Au lieu d'appeler Equals, il est préférable d'utiliser un
IComparer<T>
- et si vous n'avez plus d'informations,EqualityComparer<T>.Default
c'est un bon choix:Mis à part toute autre chose, cela évite la boxe / casting.
la source
En général,
EqualityComparer<T>.Default.Equals
devrait faire le travail avec tout ce qui met en œuvreIEquatable<T>
, ou qui a un sensEquals
implémentation .Si, cependant,
==
etEquals
sont mis en œuvre différemment pour une raison quelconque, alors mon travail sur les opérateurs génériques devrait être utile; il prend en charge les versions opérateur de (entre autres):la source
Autant de réponses, et pas une seule n'explique le POURQUOI? (que Giovanni a explicitement demandé) ...
Les génériques .NET n'agissent pas comme des modèles C ++. Dans les modèles C ++, la résolution de surcharge se produit une fois que les paramètres réels du modèle sont connus.
Dans les génériques .NET (y compris C #), la résolution de surcharge se produit sans connaître les paramètres génériques réels. Les seules informations que le compilateur peut utiliser pour choisir la fonction à appeler proviennent des contraintes de type sur les paramètres génériques.
la source
==
fonctionne pour tous les types, que ce soit des types de référence ou des types de valeur. Telle devrait être la question à laquelle je ne pense pas que vous ayez répondu.==
ne fonctionne pas pour tous les types de valeur. Plus important encore, il n'a pas la même signification pour tous les types, donc le compilateur ne sait pas quoi en faire.==
. Pouvez-vous également inclure cette partie dans votre réponse, car je suppose que c'est le point principal iciLa compilation ne peut pas savoir que T ne peut pas être un struct (type de valeur). Il faut donc lui dire que ça ne peut être que de type référence je pense:
C'est parce que si T pourrait être un type de valeur, il pourrait y avoir des cas où il
x == y
serait mal formé - dans les cas où un type n'a pas d'opérateur == défini. La même chose se produira pour ce qui est plus évident:Cela échoue également, car vous pourriez passer un type T qui n'aurait pas de fonction foo. C # vous oblige à vous assurer que tous les types possibles ont toujours une fonction foo. Cela se fait par la clause where.
la source
Il semble que sans la contrainte de classe:
Il faut se rendre compte que tandis que
class
contraintEquals
dans l'==
opérateur hérite deObject.Equals
, tandis que celui d'une structure remplaceValueType.Equals
.Notez que:
donne également la même erreur de compilation.
Pour l'instant, je ne comprends pas pourquoi le compilateur rejette une comparaison d'opérateurs d'égalité de type valeur. Je sais cependant que cela fonctionne:
la source
Object.Equals
mais les tests à la place l' égalité de référence. Par exemple,Compare("0", 0.ToString())
retournerait false, car les arguments seraient des références à des chaînes distinctes, qui ont toutes deux un zéro comme seul caractère.NullReferenceException
pourrait se produire.Eh bien dans mon cas, je voulais tester unitairement l'opérateur d'égalité. J'avais besoin d'appeler le code sous les opérateurs d'égalité sans définir explicitement le type générique. Conseils pour le
EqualityComparer
sont pas utiles commeEqualityComparer
appeléEquals
méthode mais pas pour l'opérateur d'égalité.Voici comment j'ai pu travailler avec des types génériques en créant un fichier
LINQ
. Il appelle le bon code pour==
et les!=
opérateurs:la source
Il existe une entrée MSDN Connect pour ce ici
La réponse d'Alex Turner commence par:
la source
Si vous voulez vous assurer que les opérateurs de votre type personnalisé sont appelés, vous pouvez le faire via la réflexion. Obtenez simplement le type en utilisant votre paramètre générique et récupérez le MethodInfo pour l'opérateur souhaité (par exemple op_Equality, op_Inequality, op_LessThan ...).
Exécutez ensuite l'opérateur à l'aide de la méthode Invoke de MethodInfo et passez les objets en tant que paramètres.
Cela invoquera votre opérateur surchargé et non celui défini par les contraintes appliquées au paramètre générique. Cela pourrait ne pas être pratique, mais pourrait s'avérer utile pour tester vos opérateurs en unité lors de l'utilisation d'une classe de base générique qui contient quelques tests.
la source
J'ai écrit la fonction suivante en regardant la dernière msdn. Il peut facilement comparer deux objets
x
ety
:la source
return ((IComparable)(x)).CompareTo(y) <= 0;
Ce qui précède fonctionnera car == est pris en charge dans le cas de types de référence définis par l'utilisateur.
Dans le cas des types de valeur, == peut être remplacé. Dans ce cas, "! =" Doit également être défini.
Je pense que cela pourrait être la raison, cela interdit la comparaison générique en utilisant "==".
la source
==
jeton est utilisé pour deux opérateurs différents. Si, pour les types d'opérandes donnés, il existe une surcharge compatible de l'opérateur d'égalité, cette surcharge sera utilisée. Sinon, si les deux opérandes sont des types de référence compatibles entre eux, une comparaison de référence sera utilisée. Notez que dans laCompare
méthode ci-dessus, le compilateur ne peut pas dire que la première signification s'applique, mais peut dire que la deuxième signification s'applique, donc le==
jeton utilisera cette dernière même s'ilT
surcharge l'opérateur de vérification d'égalité (par exemple s'il est de typeString
) .Le
.Equals()
travail pour moiTKey
est un type générique.la source
x.Id.Equals
pas çaid.Equals
. Vraisemblablement, le compilateur sait quelque chose sur le type dex
.