Est-ce un bon langage / efficace pour la mise en œuvre et les opérateurs Equals l'égalité / inégalité?

StackOverflow https://stackoverflow.com/questions/1654820

  •  11-09-2019
  •  | 
  •  

Question

J'ai eu quelques problèmes pour obtenir ce droit, donc je voulais demander si quelqu'un a des commentaires sur que ce soit un moyen efficace de mettre en œuvre la méthode Equals et l'égalité opérateurs / inégalité pour une classe personnalisée immuable. Ces opérateurs sont très souvent appelés par mon programme, alors je veux vous assurer que je les ai droit.

class MyObj
{

    public static bool operator ==(MyObj a, MyObj b)
    {
        if (!object.ReferenceEquals(a, null))
            return a.Equals(b);
        else if (!object.ReferenceEquals(b, null))
            return b.Equals(a);
        else
            // both are null
            return true;
    }

    public static bool operator !=(MyObj a, MyObj b)
    {
        if (!object.ReferenceEquals(a, null))
            return !a.Equals(b);
        else if (!object.ReferenceEquals(b, null))
            return !b.Equals(a);
        else
            // both are null
            return false
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as MyObj);
    }

    public bool Equals(MyObj obj)
    {
        if (object.ReferenceEquals(obj, null))
            return false;
        else
            return (obj.FieldOne == this.FieldOne &&
                    obj.FieldTwo == this.FieldTwo && ...);
    }

}
Était-ce utile?

La solution

J'utilise le code suivant pour les types de référence, qui a moins le double emploi et se sent plus propre, à mon avis. Avoir une méthode statique « Equals » permet aux langages .NET sans surcharger l'opérateur de comparer vos instances sans avoir à tester null avant d'appeler la méthode d'instance. Si vous implémentez des opérateurs d'égalité, il pourrait également être mieux pour rendre votre classe immuable, si vous le pouvez.

class Foo : IEquatable<Foo>
{
    public override bool Equals(object obj)
    {
        return Equals(obj as Foo);
    }

    public bool Equals(Foo other)
    {
        if (object.ReferenceEquals(other, null)) return false;

        // Optional early out
        if (object.ReferenceEquals(this, other)) return true; 

        // Compare fields here
    }

    public static bool Equals(Foo a, Foo b)
    {
        if (ReferenceEquals(a, null)) return ReferenceEquals(b, null);
        return a.Equals(b);
    }

    public static bool operator ==(Foo a, Foo b)
    {
        return Equals(a, b);
    }

    public static bool operator !=(Foo a, Foo b)
    {
        return !Equals(a, b);
    }
}

Autres conseils

Certaines choses je remarque:

  • Parce que vous surchargeons Equals, vous devez également remplacer GetHashCode.
  • Étant donné que votre méthode de Equals(MyObj) est une implémentation valide pour l'ensemble de l'interface IEquatable<MyObj>, MyObj doit en effet mettre en œuvre cette interface. Cela permettra également Dictionary<> et ce à profiter directement de votre méthode de Equals(MyObj), au lieu de passer par Equals(object).

Aussi je suis entièrement d'accord avec la mise en œuvre alternative Trillian, sauf que je l'aurais mis en œuvre a != b directement !(a == b) au lieu de !Equals(a, b). (Différence Trivial bien sûr.)

En gros oui, mais il y a une erreur à corriger.

La méthode Equals(object) appelle lui-même au lieu d'appeler la méthode Equals(MyObj), ce qui provoque une boucle éternelle. Il devrait être:

public override bool Equals(object obj) {
   MyObj other = obj as MyObj;
   return this.Equals(other);
}

ou simplement:

public override bool Equals(object obj) {
   return this.Equals(obj as MyObj);
}

En outre, vous pouvez simplifier l'opérateur d'inégalité à:

public static bool operator !=(MyObj a, MyObj b) {
   return !(a == b);
}

Si vous êtes à la recherche d'efficacité, je recommande d'utiliser ce lieu de object.ReferenceEquals(foo, null):

(object)foo == null

Ceci est effectivement équivalent, mais évite un appel de fonction.

J'aime aussi mettre en œuvre IEquatable<T> dans tous mes types qui remplacent Equals. Pour les types de référence, je puis Equals(object) avec impatience Equals(Foo).

public override bool Equals(object other){return Equals(other as Foo);}

Les opérateurs peuvent surcharges être simplifiées si:

public static bool operator==(Foo a, Foo b){
    if((object)a == null)
        return (object)b == null;
    return a.Equals(b);
}
public static bool operator!=(Foo a, Foo b){
    return !(a == b);
}

Si absolu l'efficacité est nécessaire, cependant, il vaut peut-être peu de dédoublement de code dans ces fonctions pour éviter les appels de fonctions supplémentaires, mais contrairement à l'aide (object)foo == null au lieu de object.ReferenceEquals(foo, null), évitant ainsi l'appel de fonction nécessite code supplémentaire pour maintenir, de sorte que le petit gain peut ne pas valoir la peine.

Je préfère laisser toute la logique « si cela est nul alors faire d'autre ... » au cadre:

class MyObj : IEquatable<MyObj> {

  public static bool operator ==( MyObj left, MyObj right ) {
    return EqualityComparer<MyObj>.Default.Equals( left, right );
  }

  public static bool operator !=( MyObj left, MyObj right ) {
    return !EqualityComparer<MyObj>.Default.Equals( left, right );
  }

  public override bool Equals( object obj ) {
    return this.Equals( obj as MyObj );
  }

  public bool Equals( MyObj other ) {
    return !object.ReferenceEquals( other, null )
        && obj.FieldOne == this.FieldOne
        && obj.FieldTwo == this.FieldTwo
        && ...
        ;
  }

  ...

}

Voir aussi Quel est le meilleur algorithme pour une GetHashCode surchargée pour la mise en œuvre GetHashCode.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top