228 votes

Quelle est la différence entre IEquatable et la simple surcharge de Object.Equals() ?

Je veux que mon Food pour pouvoir tester chaque fois qu'elle est égale à une autre instance de la classe Food . Je l'utiliserai plus tard contre une liste, et je veux utiliser sa fonction List.Contains() méthode. Dois-je mettre en œuvre IEquatable<Food> ou simplement remplacer Object.Equals() ? De MSDN :

Cette méthode détermine l'égalité en utilisant le comparateur d'égalité par défaut, tel que défini par l'implémentation de l'objet de l'objet de l IEquatable.Equals pour T (le type des valeurs de la liste).

Ma prochaine question est donc la suivante : quelles fonctions/classes de la structure .NET font appel à Object.Equals() ? Devrais-je l'utiliser en premier lieu ?

3 votes

Une très bonne explication ici blogs.msdn.com/b/jaredpar/archive/2009/01/15/

1 votes

Duplicata possible de Comprendre IEquatable

261voto

Josh Points 38617

La raison principale est la performance. Lorsque les génériques ont été introduits dans .NET 2.0, ils ont permis d'ajouter un grand nombre de classes intéressantes telles que List<T> , Dictionary<K,V> , HashSet<T> etc. Ces structures font un usage intensif de GetHashCode et Equals . Mais pour les types de valeurs, cela nécessitait une mise en boîte. IEquatable<T> permet à une structure d'implémenter un modèle fortement typé Equals de sorte qu'aucune mise en boîte n'est nécessaire. Les performances sont donc bien meilleures lorsque vous utilisez des types de valeurs avec des collections génériques.

Les types de référence n'en profitent pas autant, mais les IEquatable<T> vous permet d'éviter un lancer de System.Object ce qui peut faire une différence si elle est appelée fréquemment.

Comme indiqué sur Le blog de Jared Parson cependant, vous doit mettent toujours en œuvre la norme Object.Equals et Object.GetHashcode dérogations.

0 votes

Y a-t-il des castings entre les types de référence ? J'ai toujours pensé que les castings étaient juste des "déclarations" que vous faites au compilateur lorsque vous attribuez des castings pas évidents d'un type d'objet à un autre. C'est-à-dire qu'après avoir compilé, le code ne saurait même pas qu'il y a eu un cast.

9 votes

C'est vrai en C++ mais pas dans les langages .NET qui appliquent la sécurité des types. Il y a un cast d'exécution et si le cast ne réussit pas, une exception est levée. Il y a donc une petite pénalité d'exécution à payer pour le casting. Le compilateur peut optimiser les upcasts Par exemple, objet o = (objet) "string" ; Mais le downcasting - string s = (string)o ; - doit se produire au moment de l'exécution.

1 votes

Je vois. Auriez-vous, par hasard, un endroit où je pourrais obtenir ce genre d'informations plus approfondies sur .NET ? Merci !

55voto

CodexArcanum Points 1724

Selon le MSDN :

Si vous mettez en œuvre IEquatable<T> vous devez également surcharger la classe de base de la classe de base Object.Equals(Object) et GetHashCode afin que leur comportement soit cohérent avec celui de la IEquatable<T>.Equals méthode. Si vous remplacez Object.Equals(Object) votre implémentation surchargée est également appelée dans les appels à l'objet statique Equals(System.Object, System.Object) sur votre classe. Cela garantit que toutes les invocations de l'adresse Equals méthode donnent des résultats résultats.

Il semble donc qu'il n'y ait pas de réelle différence fonctionnelle entre les deux, si ce n'est que l'une ou l'autre pourrait être appelée 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 elle n'est pas pénalisée par le boxing/dunboxing.

D'un point de vue logique, il est également préférable d'implémenter l'interface. Le fait de surcharger l'objet n'indique pas vraiment à qui que ce soit que votre classe est réellement équatable. La surcharge peut juste être une classe qui ne fait rien ou une implémentation superficielle. L'utilisation de l'interface dit explicitement, "Hey, cette chose est valide pour la vérification de l'égalité !" C'est tout simplement une meilleure conception.

10 votes

Les structures doivent absolument implémenter iEquatable(of theirOwnType) si elles sont utilisées comme clés dans un dictionnaire ou une collection similaire ; cela offre un gain de performance important. Les classes non héritables bénéficieront d'une légère amélioration des performances en implémentant IEquatable(of theirOwnType). Les classes héritables ne doivent //pas// implémenter IEquatable.

35voto

this. __curious_geek Points 23728

Prolongeant 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, je dispose d'une méthode Equals() réutilisable qui fonctionne d'emblée pour toutes mes classes dérivées.

0 votes

Une dernière question. Quel est l'avantage d'utiliser "obj as EntityBase" au lieu de (EntityBase)obj ? Est-ce juste une question de style ou y a-t-il un avantage quelconque ?

24 votes

Dans le cas de "obj as EntityBase" - si l'obj n'est pas de type EntityBase, il passera "null" et continuera sans aucune erreur ou exception, mais dans le cas de "(EntityBase)obj", il essaiera de convertir l'obj en EntityBase et si l'obj n'est pas de type EntityBase, il lancera InvalidCastException. Et oui, "as" ne peut être appliqué qu'aux types de référence.

0 votes

Je comprends la réponse (equals génériques - donc fortement typés, et pas besoin de les convertir en objets, etc.) Mais j'ai une question concernant la déclaration suivante : >>> De cette façon, j'ai une méthode Equals() réutilisable qui fonctionne pour toutes mes classes dérivées<<<< N'est-il pas vrai que si l'on ne met pas en œuvre 'Iequatable<T>', il suffit de surcharger 'equals', cela peut fonctionner pour les types dérivés également, n'est-ce pas ? je veux juste m'assurer de votre intention :)

2voto

Si nous appelons object.Equals cela oblige à une mise en boîte coûteuse des types de valeurs. Ceci n'est pas souhaitable dans les scénarios sensibles aux performances. La solution consiste à utiliser IEquatable<T> .

public interface IEquatable<T>
{
  bool Equals (T other);
}

L'idée derrière IEquatable<T> est qu'il donne le même résultat que object.Equals mais plus rapidement. La contrainte where T : IEquatable<T> doit être utilisé avec les types génériques comme ci-dessous.

public class Test<T> where T : IEquatable<T>
{
  public bool IsEqual (T a, T b)
  {
    return a.Equals (b); // No boxing with generic T
  }
}

sinon, il se lie à slower object.Equals() .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X