web-dev-qa-db-fra.com

Qu'est-ce qu'une "meilleure pratique" pour comparer deux instances d'un type de référence?

Je suis tombé sur cette situation récemment. Jusqu'à présent, j’ai volontiers remplacé l’opérateur d’égalité (==) et/ou Equals afin de voir si deux types de référence contenaient réellement le même data (c'est-à-dire deux instances différentes qui se ressemblent).

Je l'utilise encore plus depuis les tests automatisés (comparant les données de référence/attendues à celles renvoyées).

En examinant certaines des directives relatives aux normes de codage dans MSDN , je suis tombé sur un article qui le déconseillait. Maintenant, je comprends pourquoi l'article dit ceci (car ils ne sont pas identiques instance), mais il ne répond pas à la question suivante:

  1. Quel est le meilleur moyen de comparer deux types de référence?
  2. Devrions-nous implémenter IComparable ? (J'ai également vu mentionner que cela devrait être réservé aux types de valeur).
  3. Y a-t-il une interface que je ne connais pas?
  4. Devrions-nous rouler les nôtres?!

Merci beaucoup ^ _ ^

Mettre à jour

On dirait que j'ai mal lu une partie de la documentation (la journée a été longue) et qu'il est peut-être préférable de remplacer Equals .

Si vous implémentez des types de référence, vous devez envisager de remplacer la méthode Equals sur un type de référence si votre type ressemble à un type de base tel que Point, String, BigNumber, etc. La plupart des types de référence ne doivent pas surcharger l'opérateur égalité - même s'ils remplacent Egal. Toutefois, si vous implémentez un type de référence destiné à avoir une sémantique de valeur, telle qu'un type de nombre complexe, vous devez remplacer l'opérateur d'égalité.

44
Rob Cooper

On dirait que vous codez en C #, avec une méthode appelée Equals que votre classe doit implémenter, si vous souhaitez comparer deux objets en utilisant une métrique autre que "ces deux pointeurs (car les descripteurs d’objet ne sont-ils que des pointeurs) sont-ils la même adresse mémoire? ".

J'ai attrapé un exemple de code de ici :

class TwoDPoint : System.Object
{
    public readonly int x, y;

    public TwoDPoint(int x, int y)  //constructor
    {
        this.x = x;
        this.y = y;
    }

    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Point return false.
        TwoDPoint p = obj as TwoDPoint;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public bool Equals(TwoDPoint p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public override int GetHashCode()
    {
        return x ^ y;
    }
}

Java a des mécanismes très similaires. La méthode equals () fait partie de la classe Object , et votre classe la surcharge si vous souhaitez utiliser ce type de fonctionnalité.

La raison pour laquelle surcharger "==" peut être une mauvaise idée pour les objets est que, généralement, vous souhaitez toujours pouvoir effectuer les comparaisons "sont-elles le même pointeur". Celles-ci sont généralement utilisées, par exemple, pour insérer un élément dans une liste où aucun doublon n'est autorisé, et certains éléments de votre infrastructure peuvent ne pas fonctionner si cet opérateur est surchargé de manière non standard.

21
Matt J

Implémenter l'égalité dans .NET correctement, efficacement et sans duplication de code est difficile. Spécifiquement, pour les types de référence avec une sémantique de valeur (c'est-à-dire les types immuables qui traitent l'équvialence comme une égalité ), vous devez implémenter l'interface System.IEquatable<T> , et vous devez implémenter toutes les différentes opérations (Equals, GetHashCode et ==, != ).

A titre d'exemple, voici une classe implémentant l'égalité de valeur:

class Point : IEquatable<Point> {
    public int X { get; }
    public int Y { get; }

    public Point(int x = 0, int y = 0) { X = x; Y = y; }

    public bool Equals(Point other) {
        if (other is null) return false;
        return X.Equals(other.X) && Y.Equals(other.Y);
    }

    public override bool Equals(object obj) => Equals(obj as Point);

    public static bool operator ==(Point lhs, Point rhs) => object.Equals(lhs, rhs);

    public static bool operator !=(Point lhs, Point rhs) => ! (lhs == rhs);

    public override int GetHashCode() =>X.GetHashCode() ^ Y.GetHashCode();
}

Les seules parties mobiles du code ci-dessus sont les parties en gras: la deuxième ligne de Equals(Point other) et la méthode GetHashCode(). L'autre code devrait rester inchangé.

Pour les classes de référence qui ne représentent pas des valeurs immuables, n'implémentez pas les opérateurs == et !=. Utilisez plutôt leur signification par défaut, qui consiste à comparer l'identité de l'objet.

Le code intentionnellement égalise les objets même d'un type de classe dérivé. Souvent, cela peut ne pas être souhaitable car l’égalité entre la classe de base et les classes dérivées n’est pas bien définie. Malheureusement, .NET et les instructions de codage ne sont pas très clairs ici. Le code créé par Resharper, posté dans une autre réponse , est susceptible d'un comportement indésirable dans de tels cas, car Equals(object x) et Equals(SecurableResourcePermission x) will traitera ce cas différemment.

Afin de changer ce comportement, une vérification de type supplémentaire doit être insérée dans la méthode Equals fortement typée ci-dessus:

public bool Equals(Point other) {
    if (other is null) return false;
    if (other.GetType() != GetType()) return false;
    return X.Equals(other.X) && Y.Equals(other.Y);
}
26
Konrad Rudolph

Ci-dessous, j'ai résumé ce que vous devez faire pour implémenter IEquatable et fourni la justification à partir des différentes pages de documentation MSDN.


Résumé

  • Lorsque vous souhaitez tester l'égalité des valeurs (par exemple, lors de l'utilisation d'objets dans des collections), vous devez implémenter l'interface IEquatable, substituer Object.Equals et GetHashCode pour votre classe.
  • Lorsque vous souhaitez tester l'égalité de référence, vous devez utiliser l'opérateur ==, l'opérateur! = Et Object.ReferenceEquals .
  • Vous ne devez remplacer que l'opérateur == et l'opérateur! = Pour ValueTypes et les types de référence immuables.

Justification

IEquatable

L'interface System.IEquatable est utilisée pour comparer deux instances d'un objet pour égalité. Les objets sont comparés en fonction de la logique implémentée dans la classe. La comparaison aboutit à une valeur booléenne indiquant si les objets sont différents. Cela contraste avec l'interface System.IComparable, qui renvoie un entier indiquant en quoi les valeurs d'objet sont différentes.

L'interface IEquatable déclare deux méthodes qui doivent être remplacées. La méthode Equals contient l'implémentation permettant d'effectuer la comparaison réelle et de renvoyer true si les valeurs de l'objet sont égales ou false si elles ne le sont pas. La méthode GetHashCode doit renvoyer une valeur de hachage unique pouvant être utilisée pour identifier de manière unique des objets identiques contenant des valeurs différentes. Le type d'algorithme de hachage utilisé est spécifique à l'implémentation. 

IEquatable.Equals, méthode

  • Vous devez implémenter IEquatable pour que vos objets gèrent la possibilité qu'ils soient stockés dans un tableau ou une collection générique.
  • Si vous implémentez IEquatable, vous devez également redéfinir les implémentations de classe de base de Object.Equals (Object) et GetHashCode afin que leur comportement soit cohérent avec celui de la méthode IEquatable.Equals.

Instructions pour remplacer Equals () et Operator == (Guide de programmation C #)

  • x.Equals (x) renvoie true.
  • x.Equals (y) renvoie la même valeur que y.Equals (x)
  • si (x.Equals (y) && y.Equals (z)) renvoie true, alors x.Equals (z) renvoie true.
  • Invocations successives de x. Equals (y) renvoie la même valeur tant que les objets référencés par x et y ne sont pas modifiés.
  • x. Equals (null) renvoie false (pour les types de valeur non nullable uniquement. Pour plus d'informations, voir Types nullables (Guide de programmation C #) .) 
  • La nouvelle implémentation d'Equal ne devrait pas lancer d'exceptions.
  • Il est recommandé que toute classe qui remplace Equals substitue également Object.GetHashCode.
  • Il est recommandé qu'en plus d'implémenter Equals (objet), toute classe implémente également Equals (type) pour son propre type, afin d'améliorer les performances.

Par défaut, l'opérateur == teste l'égalité des références en déterminant si deux références indiquent le même objet. Par conséquent, les types de référence ne doivent pas nécessairement implémenter l'opérateur == pour bénéficier de cette fonctionnalité. Lorsqu'un type est immuable, c'est-à-dire que les données contenues dans l'instance ne peuvent pas être modifiées, l'opérateur de surcharge == pour comparer l'égalité de valeur au lieu d'égalité de référence peut être utile car, en tant qu'objets immuables, ils peuvent être considérés comme identiques comme ils ont la même valeur. Ce n'est pas une bonne idée de remplacer l'opérateur == dans les types non immuables.

  • Les implémentations surchargées des opérateurs == ne devraient pas lancer d'exceptions.
  • Tout type qui surcharge l'opérateur == doit également surcharger l'opérateur! =.

== Opérateur (référence C #)

  • Pour les types de valeur 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.
  • Lorsque vous testez la valeur null à l'aide de comparaisons == dans votre substitution d'opérateur ==, veillez à utiliser l'opérateur de classe d'objet de base. Si vous ne le faites pas, une récursion infinie se produira, entraînant un stackoverflow.

Méthode Object.Equals (Object)

Si votre langage de programmation prend en charge la surcharge d'opérateur et si vous choisissez de surcharger l'opérateur d'égalité pour un type donné, ce type doit remplacer la méthode Equals. De telles implémentations de la méthode Equals doivent retourner les mêmes résultats que l'opérateur d'égalité.

Les instructions suivantes permettent d’implémenter un type de valeur:

  • Envisagez de remplacer Equals pour obtenir des performances supérieures à celles fournies par l'implémentation par défaut d'Egals sur ValueType.
  • Si vous substituez Equals et que la langue prend en charge la surcharge d'opérateur, vous devez surcharger l'opérateur d'égalité pour votre type de valeur.

Les instructions suivantes permettent d’implémenter un type de référence:

  • Envisagez de remplacer Equals par un type de référence si la sémantique du type est basée sur le fait que le type représente une ou plusieurs valeurs.
  • La plupart des types de référence ne doivent pas surcharger l'opérateur d'égalité, même s'ils remplacent Equals. Toutefois, si vous implémentez un type de référence destiné à avoir une sémantique de valeur, telle qu'un type de nombre complexe, vous devez remplacer l'opérateur d'égalité.

Gotchas supplémentaires

16
Zach Burlingame

Cet article recommande simplement de ne pas redéfinir l'opérateur d'égalité (pour les types de référence), et non de redéfinir Egals. Vous devez remplacer Equals dans votre objet (référence ou valeur) si les contrôles d'égalité signifient davantage que les contrôles de référence. Si vous voulez une interface, vous pouvez aussi implémenter IEquatable (utilisé par les collections génériques). Toutefois, si vous implémentez IEquatable, vous devez également remplacer les expressions égales, comme l'indique la section Remarques d'equatable:

Si vous implémentez IEquatable <T>, vous devez également redéfinir les implémentations de classe de base de Object.Equals (Object) et GetHashCode afin que leur comportement soit cohérent avec celui de la méthode IEquatable <T> .Equals. Si vous substituez Object.Equals (Object), votre implémentation substituée est également appelée dans les appels à la méthode statique Equals (System.Object, System.Object) sur votre classe. Cela garantit que tous les invocations de la méthode Equals renvoient des résultats cohérents. 

Pour savoir si vous devez implémenter l'opérateur Equals et/ou l'égalité:

From Implémentation de la méthode Equals

La plupart des types de référence ne doivent pas surcharger l'opérateur d'égalité, même s'ils remplacent Equals.

From Instructions pour l'implémentation d'Equals et de l'opérateur d'égalité (==)

Remplacez la méthode Equals chaque fois que vous implémentez l'opérateur d'égalité (==) et faites en sorte qu'ils fassent la même chose.

Cela indique seulement que vous devez remplacer Equals chaque fois que vous implémentez l'opérateur d'égalité. Cela signifie not qu'il est nécessaire de remplacer l'opérateur d'égalité lorsque vous remplacez Egal.

3
bdukes

Pour les objets complexes qui donneront des comparaisons spécifiques, alors implémenter IComparable et définir la comparaison dans les méthodes de comparaison est une bonne implémentation.

Par exemple, nous avons des objets "Véhicule" dont la seule différence peut être le numéro d'enregistrement et que nous utilisons pour comparer les résultats afin de nous assurer que la valeur attendue renvoyée lors du test est celle que nous souhaitons.

2
Paul Shannon

J'ai tendance à utiliser ce que Resharper fait automatiquement. Par exemple, il crée automatiquement ceci pour l'un de mes types de référence:

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    return obj.GetType() == typeof(SecurableResourcePermission) && Equals((SecurableResourcePermission)obj);
}

public bool Equals(SecurableResourcePermission obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    return obj.ResourceUid == ResourceUid && Equals(obj.ActionCode, ActionCode) && Equals(obj.AllowDeny, AllowDeny);
}

public override int GetHashCode()
{
    unchecked
    {
        int result = (int)ResourceUid;
        result = (result * 397) ^ (ActionCode != null ? ActionCode.GetHashCode() : 0);
        result = (result * 397) ^ AllowDeny.GetHashCode();
        return result;
    }
}

Si vous souhaitez remplacer == et toujours effectuer des vérifications de références, vous pouvez toujours utiliser Object.ReferenceEquals.

1
mattlant

Microsoft semble avoir modifié sa mélodie, ou du moins existe-t-il des informations contradictoires sur la nécessité de ne pas surcharger l'opérateur d'égalité. Selon cet article Microsoft intitulé Comment: définir une égalité de valeurs pour un type:

"Les opérateurs == et! = Peuvent être utilisés avec des classes même si la classe ne les surcharge pas. Cependant, le comportement par défaut consiste à effectuer un contrôle de l'égalité des références. Dans une classe, si vous surchargez la méthode Equals, vous devez surcharger le == et! = opérateurs, mais ce n'est pas obligatoire. "

Selon Eric Lippert dans sa réponse à une question que j'ai posée à propos de code minimal pour l'égalité en C # - il dit:

"Le danger que vous rencontrez ici est que vous ayez un opérateur == défini pour vous qui référence l'égalité par défaut. Vous pourriez facilement vous retrouver dans une situation où une méthode Equals surchargée valorise l'égalité et == référence l'égalité, puis vous utilisez accidentellement l’égalité de référence sur des choses non-référence-égales qui sont valeur-égale.C’est une pratique sujette aux erreurs qui est difficile à déceler par une révision du code humain.

Il y a quelques années, j'ai travaillé sur un algorithme d'analyse statique pour détecter statistiquement cette situation et nous avons constaté un taux de défauts d'environ deux instances par million de lignes de code dans toutes les bases de code que nous avons étudiées. En considérant seulement les bases de code qui avaient quelque part dépassé Equals, le taux de défauts était évidemment beaucoup plus élevé!

De plus, considérez les coûts et les risques. Si vous avez déjà des implémentations de IComparable, l'écriture de tous les opérateurs est un one-liners trivial qui ne comportera pas de bogue et ne sera jamais modifié. C'est le code le moins cher que vous allez écrire. Si on me donne le choix entre le coût fixe d’écriture et de test d’une douzaine de méthodes minuscules par rapport au coût sans bornes de la recherche et de la correction d’un bogue difficile à voir dans lequel l’égalité de référence est utilisée à la place de l’égalité de valeur, je sais laquelle choisir.

Le .NET Framework n'utilisera jamais == ou! = Avec aucun type que vous écrivez. Mais le danger est ce qui arriverait si quelqu'un d'autre le faisait. Donc, si le cours est pour une 3ème partie, je fournirais toujours les opérateurs == et! =. Si la classe est uniquement destinée à être utilisée en interne par le groupe, je mettrais probablement encore en œuvre les opérateurs == et! =.

Je ne mettrais en œuvre les opérateurs <, <=,> et> = que si IComparable était implémenté. IComparable ne devrait être implémenté que si le type doit prendre en charge la commande - comme lors du tri ou utilisé dans un conteneur générique commandé tel que SortedSet.

Si le groupe ou l'entreprise avait une politique en place pour ne jamais implémenter les opérateurs == et! =, Alors je suivrais bien sûr cette politique. Si une telle stratégie était en place, il serait alors judicieux de l'appliquer avec un outil d'analyse de code Q/A qui signale toute occurrence des opérateurs == et! = Lorsqu'ils sont utilisés avec un type de référence.

1
Bob Bryan

Il est remarquable de constater combien il est difficile de bien faire les choses.

La recommandation de Microsoft d'avoir Equals et == faire des choses différentes dans ce cas n'a pas de sens pour moi. À un moment donné, quelqu'un s'attendra (à juste titre) à Equals et == à produire le même résultat et le code bombardera.

Je cherchais une solution qui:

  • produire le même résultat, que Equals ou == soit utilisé dans tous les cas
  • être totalement polymorphe (égalité dérivée des appels via les références de base) dans tous les cas

C'est ce que j'utilise:

  class MyClass : IEquatable<MyClass> {
    public int X { get; }
    public int Y { get; }
    public MyClass(int X, int Y) { this.X = X; this.Y = Y; }

    public override bool Equals(object obj) => obj is MyClass o && X == o.X && Y == o.Y;
    public bool Equals(MyClass o) => object.Equals(this, o);
    public static bool operator ==(MyClass o1, MyClass o2) => object.Equals(o1, o2);
    public static bool operator !=(MyClass o1, MyClass o2) => !object.Equals(o1, o2);

    public override int GetHashCode() => HashCode.Combine(X, Y);
  }

Ici tout se termine dans Equals(object) qui est toujours polymorphe pour que les deux cibles soient atteintes.

Dériver comme ça:

  class MyDerived : MyClass, IEquatable<MyDerived> {
    public int Z { get; }
    public int K { get; }
    public MyDerived(int X, int Y, int Z, int K) : base(X, Y) { this.Z = Z; this.K = K; }

    public override bool Equals(object obj) => obj is MyDerived o && base.Equals(obj) && Z == o.Z && K == o.K;
    public bool Equals(MyDerived o) => object.Equals(this, o);
    public static bool operator ==(MyDerived o1, MyDerived o2) => object.Equals(o1, o2);
    public static bool operator !=(MyDerived o1, MyDerived o2) => !object.Equals(o1, o2);

    public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), Z, K);
  }

Ce qui est fondamentalement le même, sauf pour un gotcha - quand Equals(object) veut appeler base.Equals, faites attention à appeler base.Equals(object) et non pas base.Equals(MyClass) (ce qui provoquerait une récursion sans fin).

Une mise en garde ici est que Equals(MyClass) fera dans cette implémentation un peu de boxe mais la boxe/unboxing est hautement optimisée et cela vaut bien pour moi d'atteindre les objectifs ci-dessus.

démo: https://dotnetfiddle.net/lrMWV8

(Notez que c'est pour C #> 7.0)
(basé sur la réponse de Konrad)

0
kofifus

Je pense qu'obtenir quelque chose d'aussi simple que de vérifier que les objets sont bien égaux est un peu délicat avec la conception de .NET.

Pour Struct

1) Implémentez IEquatable<T>. Cela améliore sensiblement les performances.

2) Puisque vous avez maintenant votre propre Equals, remplacez GetHashCode et par souci de cohérence avec les divers substitutions de contrôle d’égalité object.Equals également.

3) Les opérateurs == et != surchargés n'ont pas besoin d'être religieux car le compilateur vous avertira si vous assimilez involontairement une structure à une autre avec un == ou un !=, mais il est bon de le faire pour être cohérent avec les méthodes Equals.

public struct Entity : IEquatable<Entity>
{
    public bool Equals(Entity other)
    {
        throw new NotImplementedException("Your equality check here...");
    }

    public override bool Equals(object obj)
    {
        if (obj == null || !(obj is Entity))
            return false;

        return Equals((Entity)obj);
    }

    public static bool operator ==(Entity e1, Entity e2)
    {
        return e1.Equals(e2);
    }

    public static bool operator !=(Entity e1, Entity e2)
    {
        return !(e1 == e2);
    }

    public override int GetHashCode()
    {
        throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
    }
}

Pour la classe

De MS:

La plupart des types de référence ne doivent pas surcharger l'opérateur d'égalité, même s'ils remplacent Equals.

Pour moi, == ressemble à une égalité de valeur, plus à un sucre syntaxique pour la méthode Equals. Ecrire a == b est beaucoup plus intuitif que d'écrire a.Equals(b). Rarement, nous devrons vérifier l’égalité des références. Dans les niveaux abstraits traitant de représentations logiques d'objets physiques, ce n'est pas quelque chose que nous devrions vérifier. Je pense qu'avoir une sémantique différente pour == et Equals peut être déroutant. Je crois que cela aurait dû être == pour l'égalité de valeur et Equals pour référence (ou un meilleur nom comme IsSameAs) égalité en premier lieu. J'aimerais ne pas prendre les directives de la SEP au sérieux, pas seulement parce que ce n'est pas naturel pour moi, mais aussi parce que surcharger == ne fait pas de mal majeur. C'est différent de ne pas remplacer Equals ou GetHashCode non générique, ce qui peut provoquer des dégâts, car le framework n'utilise pas == nulle part, mais seulement si nous l'utilisons nous-même. Le seul avantage réel que je tire de ne pas surcharger == et !=sera la cohérence avec la conception de l’ensemble du cadre sur lequel je n’ai aucun contrôle. Et c’est vraiment une grande chose, donc, malheureusement, je vais m'en tenir à cela .

Avec sémantique de référence (objets mutables)

1) Remplacez Equals et GetHashCode.

2) L'implémentation de IEquatable<T> n'est pas obligatoire, mais sera agréable si vous en avez un.

public class Entity : IEquatable<Entity>
{
    public bool Equals(Entity other)
    {
        if (ReferenceEquals(this, other))
            return true;

        if (ReferenceEquals(null, other))
            return false;

        //if your below implementation will involve objects of derived classes, then do a 
        //GetType == other.GetType comparison
        throw new NotImplementedException("Your equality check here...");
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Entity);
    }

    public override int GetHashCode()
    {
        throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
    }
}

Avec sémantique de valeur (objets immuables)

C'est la partie la plus délicate. Peut être facilement foiré si pas pris en charge ..

1) Remplacez Equals et GetHashCode.

2) surcharge == et != pour correspondre à Equals. Assurez-vous que cela fonctionne pour les NULL .

2) L'implémentation de IEquatable<T> n'est pas obligatoire, mais sera agréable si vous en avez un.

public class Entity : IEquatable<Entity>
{
    public bool Equals(Entity other)
    {
        if (ReferenceEquals(this, other))
            return true;

        if (ReferenceEquals(null, other))
            return false;

        //if your below implementation will involve objects of derived classes, then do a 
        //GetType == other.GetType comparison
        throw new NotImplementedException("Your equality check here...");
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Entity);
    }

    public static bool operator ==(Entity e1, Entity e2)
    {
        if (ReferenceEquals(e1, null))
            return ReferenceEquals(e2, null);

        return e1.Equals(e2);
    }

    public static bool operator !=(Entity e1, Entity e2)
    {
        return !(e1 == e2);
    }

    public override int GetHashCode()
    {
        throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
    }
}

Prenez un soin particulier pour voir ce qu'il en coûtera si votre classe peut être héritée, dans ce cas, vous devrez déterminer si un objet de classe de base peut être égal à un objet de classe dérivé. Idéalement, si aucun objet de classe dérivée n'est utilisé pour la vérification d'égalité, une instance de classe de base peut être égale à une instance de classe dérivée. Dans ce cas, il n'est pas nécessaire de vérifier l'égalité Type dans la variable générique Equals de la classe de base.

En général, veillez à ne pas dupliquer le code. J'aurais pu créer une classe de base abstraite générique (IEqualizable<T> ou environ) comme modèle pour permettre une réutilisation plus facile, mais malheureusement en C #, cela m'empêche de dériver de classes supplémentaires.

0
nawfal