Come posso implementare IEqualityComparer su una struttura di coppia generica immutabile?

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

  •  02-07-2019
  •  | 
  •  

Domanda

Attualmente ho questo (modificato dopo aver letto i consigli):

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);
    }
}

Il problema è che First e Second potrebbero non essere tipi di riferimento (VS in realtà mi avvisa di questo), ma il codice continua a essere compilato. Devo lanciarli (Primo e Secondo) su oggetti prima di confrontarli, o c'è un modo migliore per farlo?

Modifica: Nota che voglio questa struttura per supportare valore e tipi di riferimento (in altre parole, il vincolo per classe non è una soluzione valida)

Modifica 2: Per quanto riguarda ciò che sto cercando di ottenere, voglio che funzioni in un dizionario. In secondo luogo, l'SRP non è importante per me in questo momento perché non è davvero l'essenza di questo problema, ma può sempre essere riformattato in seguito. In terzo luogo, il confronto con il valore predefinito (T) non funzionerà al posto del confronto con null - provalo.

È stato utile?

Soluzione

Sembra invece che tu abbia bisogno di IEquatable:

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);
    }
  }
}

Altri suggerimenti

L'implementazione di IEqualityComparer dovrebbe essere una classe diversa (e sicuramente non una struttura in quanto si desidera riutilizzare il riferimento).

Inoltre, il tuo hashcode non dovrebbe mai essere memorizzato nella cache, poiché l'implementazione GetHashcode predefinita per una struttura (che non sostituisci) terrà conto di quel membro.

Se usi i codici hash per confrontare i metodi, dovresti controllare "quotly value" " se i codici hash sono uguali.

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;

Ma c'è un piccolo problema con il confronto tra " _first " e " _secondo " campi. Per impostazione predefinita, i tipi di riferimento utilizzano l'uguaglianza comparativa confrontando "object.ReferenceEquals" metodo, gemma possono sovrascriverli. Quindi la soluzione corretta dipende da "cosa dovrebbe fare esattamente" il tuo metodo di confronto. Dovrebbe usare " Equals " metodo del "primo" & Amp; & Quot; _second " campi o object.ReferenceEquals? O qualcosa di più complesso?

Per quanto riguarda l'avvertimento, puoi usare default (T) e default (K) invece di null.

Non riesco a vedere cosa stai cercando di ottenere, ma non dovresti usare l'hashcode per confrontare per uguaglianza - non c'è garanzia che due oggetti diversi non abbiano lo stesso hashcode. Anche se la tua struttura è immutabile, i membri _stirst e _second non lo sono.

Prima di tutto questo codice viola il principio SRP. Classe di coppia utilizzata per contenere coppie se gli oggetti, giusto? Non è corretto delegare l'uguaglianza confrontando la funzionalità con essa.

Ora diamo un'occhiata al tuo codice:

Il metodo Equals fallirà se uno degli argomenti è null - non va bene. Equals utilizza il codice hash della classe Pair, ma dai un'occhiata alla definizione di GetHashCode, è solo una combinazione di codici hash dei membri della coppia - non ha nulla a che fare con l'uguaglianza degli elementi. Mi aspetto che il metodo Equals confronterà i dati effettivi. Sono troppo impegnato al momento per fornire una corretta implementazione, sfortunatamente. Ma dal primo sguardo, il tuo codice sembra essere sbagliato. Sarebbe meglio se ci fornissi una descrizione di ciò che vuoi ottenere. Sono sicuro che i membri SO saranno in grado di darti alcuni consigli.

Potrei suggerire l'uso delle espressioni Lambda come parametro? questo ti permetterebbe di specificare come confrontare i tipi generici interni.

Non ricevo alcun avviso durante la compilazione di questo, ma suppongo che stai parlando del confronto == null? Un cast sembra rendere tutto questo un po 'più pulito, sì.

PS. Dovresti davvero usare una classe separata per il comparatore. Questa classe che ricopre due ruoli (essendo una coppia e confrontando le coppie) è semplicemente brutta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top