Je veux que ma classe Food
puisse tester chaque fois qu'elle est égale à une autre instance de Food
. Je l'utiliserai plus tard contre une liste, et je veux utiliser sa méthode List.Contains()
. Devrais-je implémenter IEquatable<Food>
Ou juste écraser Object.Equals()
? De MSDN:
Cette méthode détermine l'égalité à l'aide du comparateur d'égalité par défaut, défini par l'implémentation de l'objet de la méthode IEquatable.Equals pour T (type de valeurs de la liste).
Ma question suivante est donc la suivante: quelles fonctions/classes du framework .NET utilisent Object.Equals()
? Devrais-je l'utiliser en premier lieu?
La raison principale est la performance. Lorsque les génériques ont été introduits dans .NET 2.0, ils ont été en mesure d’ajouter de nombreuses classes intéressantes telles que List<T>
, Dictionary<K,V>
, HashSet<T>
, etc. Ces structures utilisent beaucoup GetHashCode
et Equals
. Mais pour les types de valeur, cela nécessite une boxe. IEquatable<T>
permet à une structure d'implémenter une méthode _ fortement typée Equals
afin qu'aucune boxe ne soit requise. Donne donc de bien meilleures performances lors de l’utilisation de types de valeur avec des collections génériques.
Les types de référence ne sont pas aussi bénéfiques que le IEquatable<T>
La mise en œuvre vous permet d'éviter un casting de System.Object
qui peut faire la différence si on l’appelle fréquemment.
Comme indiqué sur le blog de Jared Parson , vous devez néanmoins implémenter les substitutions d'objet.
Selon le MSDN :
Si vous implémentez
IEquatable<T>
, Vous devez également redéfinir les implémentations de classe de base deObject.Equals(Object)
etGetHashCode
de sorte que leur comportement soit cohérent avec celui de la méthodeIEquatable<T>.Equals
. . Si vous substituezObject.Equals(Object)
, votre implémentation substituée est également appelée dans les appels à la méthode statiqueEquals(System.Object, System.Object)
de votre classe. Cela garantit que tous les invocations de la méthodeEquals
renvoient des résultats cohérents.
Il semble donc qu'il n'y ait pas de réelle différence fonctionnelle entre les deux, si ce n'est que l'un ou l'autre pourrait être appelé en fonction de l'utilisation de la classe. Du point de vue des performances, il est préférable d’utiliser la version générique car aucune pénalité de boxing/unboxing n’y est associée.
D'un point de vue logique, il est également préférable de mettre en œuvre l'interface. Redéfinir l'objet ne dit à personne que votre classe est réellement équitable. La substitution peut simplement être une classe ne rien faire ou une implémentation superficielle. Utiliser l'interface dit explicitement: "Hé, cette chose est valable pour la vérification de l'égalité!" C'est juste un meilleur design.
Étendre ce que Josh a dit avec un exemple pratique. +1 à Josh - J'étais sur le point d'écrire la même chose dans ma réponse.
public abstract class EntityBase : IEquatable<EntityBase>
{
public EntityBase() { }
#region IEquatable<EntityBase> Members
public bool Equals(EntityBase other)
{
//Generic implementation of equality using reflection on derived class instance.
return true;
}
public override bool Equals(object obj)
{
return this.Equals(obj as EntityBase);
}
#endregion
}
public class Author : EntityBase
{
public Author() { }
}
public class Book : EntityBase
{
public Book() { }
}
De cette façon, j'ai la méthode réutilisable Equals () qui fonctionne sans problème pour toutes mes classes dérivées.