2009-10-18 6 views
2

Quelle décision dois-je prendre à propos de Equals(), ReferenceEquals() et == à partir des résultats suivants? Que produisent-ils réellement?Valeur par rapport à la référence

#region 
int integer = 1; 
int integer2 = integer; 

bool referenceEquality = (integer == integer2);//true 
bool valueEquality = integer.Equals(integer2);//true 
bool valueEqualityMore = object.Equals(integer, integer2);//true 
bool valueEqualityMoreMore = object.ReferenceEquals(integer, integer2);//false 
#endregion 

#region 
int integer = 1; 
int integer2 = 1; 

bool referenceEquality = (integer == integer2);//true 
bool valueEquality = integer.Equals(integer2);//true 
bool valueEqualityMore = object.Equals(integer, integer2);//true 
bool valueEqualityMoreMore = object.ReferenceEquals(integer, integer2);//false 
#endregion 

#region 
MyClass obj = new MyClass(1, "Hello"); 
MyClass obj2 = obj; 

bool referenceEquality = (obj == obj2);//true 
bool valueEquality = obj.Equals(obj2);//true 
bool valueEqualityMore = object.Equals(obj, obj2);//true 
bool valueEqualityMoreMore = object.ReferenceEquals(obj, obj2);//true    
#endregion 

#region 
MyClass obj = new MyClass(1, "Hello"); 
MyClass obj2 = new MyClass(1, "Hello"); 

bool referenceEquality = (obj == obj2);//false 
bool valueEquality = obj.Equals(obj2);//false 
bool valueEqualityMore = object.Equals(obj, obj2);//false 
bool valueEqualityMoreMore = object.ReferenceEquals(obj, obj2);//false 
#endregion 

Hell! Je ne comprends rien.

Pour moi, referenceEquals() du 1er bloc doit renvoyer true. == dans le second bloc doit retourner false (car les références sont différentes). Et, les deux Equals() dans le 4ème bloc devraient renvoyer vrai (car leurs valeurs sont identiques).

+1

Pouvez-vous préciser votre question, en particulier: 1) de ces résultats ne vous trouvez surprenant 2) De quelle décision parlez-vous – Mathias

+0

Bon commentaire. Voir la mise à jour. – anonymous

+1

referenceEquals compare la référence des instances d'objet, puisque les valeurs int sont des boîtes de deux objets différents dont la référence est différente. == Par défaut, comparez les références afin que les deux instances de MyClass, même ayant le même état, aient une référence différente car ce sont deux objets différents. – Elisha

Répondre

8

Le premier point de confusion que vous semblez avoir est que des types de valeur, à savoir int, float, DateTime, l'opérateur == est la valeur d'égalité. Avec les types de référence, == est (par défaut, voir ci-dessous) l'égalité de référence. Cela expliquerait la disparité des réponses dans vos deux premiers cas entiers.

Deuxièmement, l'implémentation par défaut de Equals() teste l'égalité de référence, pas l'égalité des valeurs. Donc, puisqu'il semble que MyClass ne remplace pas Equals(), cela explique la disparité des réponses entre vos cas de référence.

En outre, de nombreux types de référence, tels que String, remplacent l'opérateur == pour fournir une sémantique d'égalité des valeurs. Donc, votre meilleur pari, jusqu'à ce que vous mémorisez quels types sont quels, est de rechercher la documentation.

En bref:

  • types de valeur
    • == est la valeur de l'égalité (pour les types de cadres)
    • Pas un type de référence, de sorte que l'égalité de référence est dénuée de sens
  • Référence types
    • ReferenceEquals() est toujours référence l'égalité
    • == est par défaut, mais peut être (et est souvent l'égalité de référence par défaut, mais peut être (et est souvent pour les types de cadres) écarté pour assurer l'égalité de valeur
    • Equals() est l'égalité de référence pour les types de cadres) surchargées pour fournir une valeur d'égalité
+0

Une belle mise à jour de tout cela peut être trouvée ici: http: // stackoverflow .com/questions/384294/où-est-la-implémentation-de-internalequalsobject-obja-object-objb, qui montre le code C++ de ce qui se passe sous le capot. Fondamentalement, il montre explicitement que les types de valeur sont traités spécialement par 'Equals'. – Abel

1

ReferenceEquals: deux objets sont les mêmes si elles pointent à la même place dans la mémoire. Ce n'est jamais le cas pour deux types de valeur distincts. Deux objets sont égaux si la substitution d'égalité équivaut à une égalité. Cela signifie généralement que ReferenceEqual renvoie true et que les propriétés retournent true. Equals se comporte souvent différemment par objet ou struct. Remarque: chaque type de valeur intégré (int, double, IntPtr) a remplacé Equals, c'est pourquoi il se comporte différemment pour les types de valeurs (compare le contenu) puis avec ReferenceEquals (compare les adresses).

==: retourne true si deux objets ont des références égales, ou si deux types de valeur ont une teneur égale. Les types de valeur encadrés ne sont pas encadrés avant d'être comparés, à moins qu'ils ne soient explicitement transtypés en un objet.

Notez que votre new MyClass(...) a renvoyé false avec Equals. En effet, l'implémentation de MyClass n'a pas remplacé la méthode Equals. Résultat: il se comporte comme le ReferenceEquals.

Mise à jour: ajout note sur Equals pour les types de valeur

+0

Et qu'est-ce qui est arrivé à ReferenceEquals()? – anonymous

+0

Et, pourquoi, int entier = 1; int entier2 = 1; bool referenceEquality = (entier == entier2); // vrai – anonymous

+0

'==' se comporte différemment, puis appelle 'ReferenceEquals' directement.Pour les types de valeur, il se comporte de la même manière que si vous appelez 'Equals'. – Abel

1

ReferenceEquals vérifie si deux références d'objet font référence au même objet. Autrement dit, ils pointent vers le même emplacement en mémoire.

Equals est une méthode virtuelle, il peut en pratique être redéfinie à faire quoi que ce soit. Le but de la méthode est cependant de comparer les instances d'une manière qui a du sens pour le type, de quelque manière que ce soit. Si Equals n'est pas substituée, l'implémentation de object.Equals est utilisée, ce qui équivaut à ReferenceEquals.

== est l'opérateur d'égalité, qui, par défaut compare l'égalité de référence pour les instances de types de référence (par exemple les classes, les types de valeurs en boîte, interfaces), et l'égalité de valeur (mêmes valeurs pour les champs) pour les instances de types de valeur (c.-à-struct) . == peut être surchargé pour fournir un comportement personnalisé, mais ce n'est pas virtuel comme Equals.

Utilisation d'un int comme object, par exemple en le faisant passer à une méthode comme ReferenceEquals qui a object paramètres, les boîtes du int, la création d'un objet sur le tas avec le nombre entier à l'intérieur. object.ReferenceEquals(integer1, integer2) signifie essentiellement object.ReferenceEquals((object)integer1, (object)integer2).

Comparaison des différentes instances en boîte du même nombre entier par l'égalité de référence (ReferenceEquals ou ==) donnera false, tandis que la valeur d'égalité (Equals) donnera true. Pour les entiers identiques non mis en boîte, qui sont des types de valeur, == et Equals tous deux comparent l'égalité des valeurs et donneront donc true.

1

Par défaut, operator== se comporte comme suit lorsqu'il est utilisé comme a==b. Notez que operator== peut être remplacé pour effectuer n'importe quoi.

  • Si a est un type de valeur, alors il est compilé sous la forme a.Equals(b).
  • Si a est un type de référence, il est compilé sous la forme object.Equals(a,b).

object.Equals(a, b) ne fonctionne que sur les types de référence et effectue les opérations suivantes:

  • Si a est null, puis revenez (b==null)
  • Sinon, appeler a.Equals(b)

object.ReferenceEquals(a, b) ne fonctionne que sur les types de référence. Une référence d'objet dans C#/.NET est maintenue en interne sous la forme d'un pointeur. Cette méthode renvoie true si les références a et b sont identiques, c'est-à-dire si elles pointent en interne vers le même objet.

Les types de valeurs existent sous deux formes: sans boîte et en boîte. Pour les cas suivants discuter des types de valeur, je vais utiliser les variables suivantes:

int unboxed = 3; 
// boxing occurs automatically when a value type is cast to object or 
// to an interface. This allocates memory on the heap to store the value 
// and places a reference (internally a pointer) to this memory in boxed. 
object boxed = unboxed; 

À ce stade, boxed se comporte comme un type de référence. Deux valeurs encadrées peuvent ne pas être au même emplacement dans la mémoire, comme vous le voyez dans votre appel à object.ReferenceEquals(integer1, integer2) (la mise en boîte automatique se produit en raison de la conversion de chaque paramètre en object). Lorsque vous appelez object.Equals(integer1, integer2), les valeurs sont encadrées, mais comme la forme encadrée de integer1 n'est pas null, l'appel se comporte comme integer1.Equals((object)integer2), ce qui renvoie true car les valeurs encadrées sont toutes les deux 1. (Voir la note ci-dessous sur pourquoi j'ai une distribution manuelle ici).

Note sur la distribution manuelle ci-dessus: System.Int32, ainsi que la plupart des autres types de valeur, a une méthode Int32.Equals(Int32 other), afin d'appeler integer1.Equals(integer2) ne serait pas la valeur de la boîte integer2. Cela a un grand impact (positif) sur les performances pour les types de valeur légers.

1

J'ai fait des corrections sur ce que sont les comparaisons réelles effectuées. J'ai ajouté les mêmes comparaisons pour tous les cas, car vous ne faisiez qu'Equals (int) sur l'entier (qui utilise l'opérateur ==), pas .Equals (object) comme pour les autres types. J'ai aussi ajouté des pièces moulées explicites pour les paramètres pour montrer que des pièces moulées implicites sont causées en les utilisant comme paramètres:

int integer = 1; 
int integer2 = 1; // exactly the same result as copying integer 

bool valueEquality = (integer == integer2); //true 
bool valueEquality2 = integer.Equals(integer2); //true 
bool typeAndValueEquality = integer.Equals((object)integer2); //true 
bool typeAndValueEquality2 = object.Equals((object)integer, (object)integer2); //true 
bool referenceEquality = object.ReferenceEquals((object)integer, (object)integer2); //false 

// 

MyClass obj = new MyClass(1, "Hello"); 
MyClass obj2 = obj; 

bool referenceEquality = (obj == obj2); //true 
bool referenceEquality2 = obj.Equals((object)obj2); //true 
bool typeAndReferenceEquality = object.Equals((object)obj, (object)obj2); //true 
bool referenceEquality3 = object.ReferenceEquals((object)obj, (object)obj2); //true 

// 

MyClass obj = new MyClass(1, "Hello"); 
MyClass obj2 = new MyClass(1, "Hello"); 

bool referenceEquality = (obj == obj2); //false 
bool referenceEquality2 = obj.Equals((object)obj2); //false 
bool typeAndReferenceEquality = object.Equals((object)obj, (object)obj2); //false 
bool referenceEquality = object.ReferenceEquals((object)obj, (object)obj2); //false 
+0

@ Guffa, Très bien. J'ai accepté votre réponse si vous l'avez déjà posté. – anonymous

Questions connexes