Comment implémenter IEqualityComparer sur une structure Pair générique immuable?

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

  •  02-07-2019
  •  | 
  •  

Question

Actuellement, j'ai ceci (édité après avoir lu des conseils):

struct Pair<T, K> : IEqualityComparer<Pair<T, K>>
{
    readonly private T _first;
    readonly private K _second;

    public Pair(T first, K second)
    {
        _first = first;
        _second = second;

    }

    public T First { get { return _first; } }
    public K Second { get { return _second; } }

    #region IEqualityComparer<Pair<T,K>> Members

    public bool Equals(Pair<T, K> x, Pair<T, K> y)
    {
        return x.GetHashCode(x) == y.GetHashCode(y);
    }

    public int GetHashCode(Pair<T, K> obj)
    {
        int hashCode = obj.First == null ? 0 : obj._first.GetHashCode();

        hashCode ^= obj.Second == null ? 0 : obj._second.GetHashCode();

        return hashCode;
    }

    #endregion

    public override int GetHashCode()
    {
        return this.GetHashCode(this);
    }

    public override bool Equals(object obj)
    {
        return (obj != null) && 
    (obj is Pair<T, K>) && 
    this.Equals(this, (Pair<T, K>) obj);
    }
}

Le problème est que First et Second ne sont peut-être pas des types de référence (VS m'en avertit réellement), mais le code est toujours compilé. Devrais-je les jetter (premier et second) sur des objets avant de les comparer, ou existe-t-il un meilleur moyen de le faire?

Modifier: Notez que je veux que cette structure prenne en charge les types valeur et référence (en d’autres termes, la contrainte par classe n’est pas une solution valide)

Modifier 2: En ce qui concerne ce que j'essaie de réaliser, je veux que cela fonctionne dans un dictionnaire. Deuxièmement, le PRS n’est pas important pour moi pour le moment car ce n’est pas vraiment l’essence de ce problème - il peut toujours être refactoré plus tard. Troisièmement, comparer avec défaut (T) ne fonctionnera pas au lieu de comparer avec nul - essayez-le.

Était-ce utile?

La solution

Il semble que vous ayez besoin d'IEquatable à la place:

internal struct Pair<T, K> : IEquatable<Pair<T, K>>
{
  private readonly T _first;
  private readonly K _second;

  public Pair(T first, K second)
  {
    _first = first;
    _second = second;
  }

  public T First
  {
    get { return _first; }
  }

  public K Second
  {
    get { return _second; }
  }

  public bool Equals(Pair<T, K> obj)
  {
    return Equals(obj._first, _first) && Equals(obj._second, _second);
  }

  public override bool Equals(object obj)
  {
    return obj is Pair<T, K> && Equals((Pair<T, K>) obj);
  }

  public override int GetHashCode()
  {
    unchecked
    {
      return (_first != null ? _first.GetHashCode() * 397 : 0) ^ (_second != null ? _second.GetHashCode() : 0);
    }
  }
}

Autres conseils

Votre implémentation IEqualityComparer doit être une classe différente (et certainement pas une structure car vous souhaitez réutiliser la référence).

De plus, votre hashcode ne doit jamais être mis en cache, car l'implémentation GetHashcode par défaut d'une structure (que vous ne substituez pas) prendra ce membre en compte.

Si vous utilisez des codes de hachage dans les méthodes de comparaison, vous devez vérifier la valeur de "valeur réelle". si les codes de hachage sont les mêmes.

bool result = ( x._hashCode == y._hashCode );
if ( result ) { result = ( x._first == y._first && x._second == y._second ); }
// OR?: if ( result ) { result = object.Equals( x._first, y._first ) && object.Equals( x._second, y._second ); }
// OR?: if ( result ) { result = object.ReferenceEquals( x._first, y._first ) && object.Equals( x._second, y._second ); }
return result;

Mais la comparaison de " _first " et " _second " des champs. Par défaut, les types de référence utilisent l’égalité avant pour comparer " object.ReferenceEquals " méthode, bourgeon ils peuvent les remplacer. Donc, la solution correcte dépend de ce que vous devez faire exactement. la votre méthode de comparaison. Devrait utiliser & Equals " méthode de la " _first " & amp; " _second " champs, ou object.ReferenceEquals? Ou quelque chose de plus complexe?

En ce qui concerne l'avertissement, vous pouvez utiliser les valeurs par défaut (T) et par défaut (K) au lieu de null.

Je ne vois pas ce que vous essayez d'atteindre, mais vous ne devriez pas utiliser le hashcode pour faire la comparaison, il n'y a aucune garantie que deux objets différents n'auront pas le même hashcode. De plus, même si votre structure est immuable, les membres _first et _second ne le sont pas.

Tout d’abord, ce code viole le principe SRP. La classe de paires utilisée pour contenir des paires si des éléments, non? Il est incorrect de déléguer la fonctionnalité de comparaison d'égalité à celle-ci.

Ensuite, regardons votre code:

La méthode Equals échouera si l'un des arguments est null - pas bon. Equals utilise le code de hachage de la classe Pair, mais jetez un coup d'œil à la définition de GetHashCode. Il s'agit simplement d'une combinaison de codes de hachage des membres de la paire. Cela n'a rien à voir avec l'égalité des éléments. Je m'attendrais à ce que la méthode Equals compare les données réelles. Je suis trop occupé pour le moment pour assurer une mise en œuvre correcte, malheureusement. Mais dès le premier regard, votre code semble être faux. Il serait préférable que vous nous fournissiez une description de ce que vous voulez réaliser. Je suis sûr que les membres SO pourront vous donner des conseils.

Puis-je suggérer l'utilisation d'expressions Lambda en tant que paramètre? cela vous permettrait de spécifier comment comparer les types génériques internes.

Je ne reçois aucun avertissement lors de la compilation à ce sujet, mais je suppose que vous parlez de la comparaison == null? Un casting semble rendre tout cela un peu plus propre, oui.

PS. Vous devriez vraiment utiliser une classe séparée pour le comparateur. Cette classe qui remplit deux rôles (être une paire et comparer des paires) est tout simplement laide.

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