Ne pouvez pas l'opérateur == être appliquée à des types génériques en C#?
Selon la documentation de la ==
opérateur dans MSDN,
Prédéfinis pour différents types de valeurs, l'
opérateur d'égalité (==) retourne vrai si
les valeurs de ses opérandes sont égaux,
sinon false. Pour les types référence
autres que string, == retourne true si
ses deux opérandes reportez-vous à la même
objet. Pour le type de chaîne, ==
compare les valeurs des chaînes de caractères.
Valeur définie par l'utilisateur les types de surcharge
l'opérateur = = (voir opérateur). Donc peut
défini par l'utilisateur des types de référence, bien que
par défaut == se comporte comme décrit
ci-dessus pour les deux prédéfinis et
défini par l'utilisateur des types référence.
Alors pourquoi ce bout de code ne parviennent pas à compiler?
bool Compare<T>(T x, T y) { return x == y; }
Je reçois le message d'erreur Opérateur '==' ne peut pas être appliquée à des opérandes de type 'T' et 'T'. Je me demande pourquoi, depuis aussi loin que je comprends le ==
opérateur est prédéfini pour tous les types?
Edit: Merci, tout le monde. Je n'avais pas remarqué au premier abord que la déclaration a propos des types de référence seulement. Je pensais aussi que bit-par-bit de comparaison est fourni pour tous les types de valeur, je sais maintenant est pas correcte.
Mais, dans le cas où je suis en utilisant un type de référence, le ==
opérateur prédéfini référence de comparaison, ou d'utiliser la version surchargée de l'opérateur si un type défini un?
Edit 2: Par essais et erreurs, nous avons appris que le ==
opérateur utilisera le prédéfinies de référence de comparaison lors de l'utilisation sans restriction de type générique. En fait, le compilateur utilisera la meilleure méthode, il peut trouver de l'restreinte type d'argument, mais ne cherchez pas plus loin. Par exemple, le code ci-dessous, imprimez toujours true
, même lorsque 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); } }
Il pourrait être utile de comprendre que, même sans les génériques, il y a quelques types pour lesquels la
==
n'est pas autorisé entre les deux opérandes de même type. Cela est vrai pour struct
types (à l'exception de "pré-défini") qui ne surchargez pas le operator ==
. Comme exemple simple, essayez ceci: var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
En continuant mon propre ancien commentaire. Par exemple (voir la section autre thread), avec
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, alors vous ne pouvez pas vérifier kvp1 == kvp2
parce que KeyValuePair<,>
est une struct, il n'est pas un C# pré-défini le type de, et il ne surcharge pas le operator ==
. Encore un exemple en est donné par var li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
avec qui vous ne pouvez pas faire e1 == e2
(ici, nous avons le imbriquée struct List<>.Enumerator
(appelé "List`1+Enumerator[T]"
par le moteur d'exécution) qui ne surcharge pas ==
).RE: "pourquoi ce bout de code ne parviennent pas à compiler?" -- Euh... parce que vous ne pouvez pas retourner un
bool
à partir d'un void
...Merci pour attraper un 10-year-old bug!
OriginalL'auteur Hosam Aly | 2008-12-24
Vous devez vous connecter pour publier un commentaire.
"...par défaut == se comporte comme décrit ci-dessus pour les deux prédéfinis et définis par l'utilisateur des types référence."
Type T n'est pas nécessairement un type de référence, de sorte que le compilateur ne peut pas faire une telle supposition.
Cependant, cela va compiler, car il est plus explicite:
Suivre supplémentaires jusqu'à la question, "Mais, dans le cas où je suis en utilisant un type de référence, serait l'opérateur == utiliser le prédéfinies de référence de comparaison, ou d'utiliser la version surchargée de l'opérateur si un type défini un?"
J'aurais pensé qu' == sur les Génériques serait d'utiliser la version surchargée, mais le test suivant démontre le contraire. Intéressant... j'aimerais savoir pourquoi! Si quelqu'un sait s'il vous plaît partager.
Sortie
En ligne:
Surchargé == appelé
Générique:
Appuyez sur n'importe quelle touche pour continuer . . .
Suivi 2
Je ne veux point changer ma méthode de comparaison de
causes de la surcharge opérateur == pour être appelé. Je suppose, sans en préciser le type (comme un où), le compilateur ne peut pas en déduire qu'il doit utiliser l'opérateur surchargé... mais je pense qu'il aurait suffisamment d'informations pour prendre une décision, même sans en préciser le type.
Re: Suivi 2: en Fait, le compilateur va lier la meilleure méthode qu'il trouve, qui est dans ce cas de Test.op_Equal. Mais si vous aviez une classe qui dérive de Test et remplace l'opérateur, puis Test de l'opérateur sera toujours appelé.
J'ai une bonne pratique que je voudrais souligner, c'est que vous devriez toujours faire la comparaison réelle à l'intérieur d'un substituée
Equals
méthode (pas dans le==
opérateur).Résolution de surcharge se produit au moment de la compilation. Alors, quand nous avons
==
entre les types génériquesT
etT
, le meilleur de surcharge est trouvé, étant donné les contraintes qui sont exercées parT
(il y a une règle spéciale qui il ne sera jamais à la case a de la valeur-type (qui donnerait un sens), donc il doit y avoir une contrainte garantissant c'est un type de référence). Dans votre Suivi 2, si tu viens avecDerivedTest
objets, etDerivedTest
dérive deTest
mais introduit une nouvelle surcharge de==
, vous aurez le "problème" de nouveau. Qui surcharge l'appelle, est de "brûler" le IL au moment de la compilation.wierdly cela semble fonctionner pour le général de types de référence (où vous pouvez vous attendre cette comparaison sur la référence à l'égalité), mais pour les chaînes, il semble également référence à l'égalité - de sorte que vous pouvez comparer 2 chaînes identiques et ayant == (quand on est dans une méthode générique avec la contrainte de classe) disent qu'ils sont différents.
OriginalL'auteur Giovanni Galbo
Comme d'autres l'ont dit, il ne fonctionne que lorsque T est contraint d'être un type de référence. Sans contraintes, vous pouvez comparer avec la valeur null, mais seulement nulle, et que la comparaison sera toujours faux pour non nullable types de valeur.
Au lieu de l'appel d'égal à Égal, il est préférable d'utiliser un
IComparer<T>
- et si vous n'avez pas plus d'informations,EqualityComparer<T>.Default
est un bon choix:Dehors de toute autre chose, cela évite de boxe et de casting.
Mineur de côté, Jon; vous pouvez noter le commentaire re pobox vs yoda sur mon post.
Astuce sympa sur l'utilisation de EqualityComparer<T>
+1 pour montrer que l'on peut comparer à null et non nullable type de valeur, il sera toujours faux
Oui, parce qu'il y a des règles spéciales pour les comparaisons avec le littéral null. Par conséquent, les "sans contraintes, vous pouvez comparer avec la valeur null, mais seulement null". C'est dans la réponse déjà. Donc, exactement pourquoi cela ne peut pas être correcte?
OriginalL'auteur Jon Skeet
En général,
EqualityComparer<T>.Default.Equals
devrait faire le travail avec tout ce qui implémenteIEquatable<T>
, ou qui a un bonEquals
mise en œuvre.Si, toutefois,
==
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 la opérateur versions de (entre autres):Intéressant (le pobox/yoda chose). Je vais me souvenir que...
L'idée est que pobox.com/~skeet toujours point sur mon site - même si elle se déplace ailleurs. J'ai tendance à poster des liens via pobox.com pour le bien de la postérité -, mais vous pouvez actuellement remplacer yoda.arachsys.com au lieu de cela.
Le problème avec pobox.com c'est que c'est un web service e-mail (ou si la société de pare-feu dit), donc il est bloqué. C'est pourquoi je ne pouvais pas suivre son lien.
"Si, toutefois, == et Égaux sont mis en œuvre différemment, pour une raison quelconque - Saint fume! Ce qui a cependant! Peut-être que j'ai juste besoin de voir un cas d'utilisation à l'effet contraire, mais une bibliothèque avec divergents est égale à la sémantique seront probablement courir dans des problèmes plus graves que de la difficulté avec les génériques.
OriginalL'auteur Marc Gravell
Nombreuses réponses, et pas un seul n'explique le POURQUOI? (Giovanni explicitement demandé)...
.NET les génériques n'agissent pas comme des modèles C++. Dans les modèles C++, résolution de surcharge se produit après que le modèle actuel et les paramètres sont connus.
.NET génériques (notamment en C#), résolution de surcharge se produit sans en connaître la réelle paramètres génériques. La seule information que le compilateur peut utiliser pour choisir la fonction à appeler provient du type de contraintes sur les paramètres génériques.
==
fonctionne pour tous les types de il de types de référence ou des types de valeur. Que devrait être la question à laquelle je ne pense pas que vous avez répondu.En fait non,
==
ne fonctionne pas pour tous les types de valeur. Plus important encore, il n'a pas la même signification pour tous les types, de sorte que le compilateur ne sait pas quoi faire avec elle.Ben, oh oui j'ai raté la coutume des structures, nous pouvons créer sans
==
. Peut-on inclure la partie de trop dans votre réponse car je suppose que c'est le point principal iciOriginalL'auteur Ben Voigt
La compilation ne peut pas savoir T ne pouvait pas être un struct (valeur type). Donc, vous avez à le faire qu'il ne peut être de type référence, je pense:
C'est parce que si T pourrait être un type de la valeur, il pourrait y avoir des cas où
x == y
serait mal formé, dans les cas où un type n'a pas un opérateur == définies. La même chose se produira pour ce qui est de plus en plus évidents:Qui ne parvient pas trop, parce que vous pouvez passer d'un type T qui n'aurait pas une fonction foo. C# vous oblige à assurez-vous que tous les types possibles de toujours avoir une fonction foo. Cela est fait par la clause where.
Hosam, j'ai testé avec cmg (mono), et il compare toujours des références. (j'.e ne pas utiliser éventuellement défini l'opérateur== pour T)
Il y a un problème avec cette solution: l'opérateur== ne peut pas être surchargé; voir cette StackOverflow question.
OriginalL'auteur Johannes Schaub - litb
Il apparaît que, sans la contrainte de classe:
L'on doit réaliser que, bien que
class
contraintEquals
dans le==
opérateur hérite deObject.Equals
, alors que celle d'un struct remplaceValueType.Equals
.Noter que:
donne aussi la même erreur du compilateur.
Encore je ne comprends pas pourquoi avoir un type de la valeur de l'égalité de l'opérateur de comparaison est rejeté par le compilateur. Je sais pour un fait, cependant, que cela fonctionne:
mais si vous n' == sur un type de valeur, le type de la valeur n'est pas necassary de mettre en œuvre l'opérateur.
Que ferait sens, litb 🙂 Il est possible que définis par l'utilisateur les structures ne pas surcharger ==, d'où le compilateur échouer.
La première méthode de comparaison n' pas utiliser
Object.Equals
mais à la place de tests de référence de l'égalité. Par exemple,Compare("0", 0.ToString())
serait de retour faux, puisque les arguments pourraient être des références à des chaînes distinctes, qui ont tous deux un zéro comme leur seul caractère.Mineur gotcha sur ce dernier, vous n'avez pas limités à des structures, donc un
NullReferenceException
pourrait se produire.OriginalL'auteur Jon Limjap
Il y a un MSDN Connecter entrée pour cette ici
Alex Turner, la réponse commence par:
OriginalL'auteur Recep
Si vous voulez vous assurer que les exploitants de votre type personnalisé sont appelés, vous pouvez le faire via la réflexion. Juste obtenir le type à l'aide de votre paramètre générique et récupérer le MethodInfo désiré pour l'opérateur (par exemple, op_Equality, op_Inequality, op_LessThan...).
Puis d'exécuter l'opérateur à l'aide de la MethodInfo de la méthode Invoke et passez-les objets comme paramètres.
Cela va appeler votre opérateur surchargé et pas celui défini par les contraintes appliquées sur le paramètre générique. Peut ne pas être pratique, mais peut s'avérer très utile pour les tests unitaires de vos opérateurs lors de l'utilisation d'une classe générique de base qui contient un couple de tests.
OriginalL'auteur Christophe
Bien dans mon cas, j'ai voulu test unitaire de 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. Conseille pour
EqualityComparer
n'étaient pas utiles en tant queEqualityComparer
appeléEquals
méthode, mais pas l'opérateur d'égalité.Ici est de savoir comment j'ai obtenu ce travail avec les types génériques par la construction d'une
LINQ
. Il appelle le bon code pour==
et!=
opérateurs:OriginalL'auteur U. Bulle
J'ai écrit la fonction suivante à la recherche au plus tard le msdn. Il peut facilement comparer deux objets
x
ety
:return ((IComparable)(x)).CompareTo(y) <= 0;
OriginalL'auteur Charlie
Ci-dessus va travailler parce que == est pris en charge en cas de défini par l'utilisateur des types référence.
Dans le cas de types de valeur, == peut être remplacée. Dans ce cas, "!=" doivent également être définies.
Je pense que ça pourrait être la raison pour laquelle, il n'autorise pas de générique de comparaison à l'aide de "==".
Le
==
jeton est utilisé pour les deux opérateurs différents. Si pour l'opérande types il existe un compatible surcharge de l'opérateur d'égalité, que la surcharge sera utilisé. Sinon, si les deux opérandes sont des types référence qui sont compatibles les uns avec les autres, une référence de comparaison sera utilisé. Notez que dans leCompare
méthode ci-dessus, le compilateur ne peut pas dire que le sens premier s'applique, mais que peut dire le second sens s'applique, de sorte que le==
jeton utilisera le dernier même siT
les surcharges de l'égalité-vérifier l'opérateur (par exemple, si il est de typeString
).OriginalL'auteur shahkalpesh