2011-01-24 10 views
7

quindi sto cercando di fare questo lavoro e io non riesco a capire perché non funzionaLINQ e distinti, l'attuazione delle pari e GetHashCode

codice demo;

namespace ConsoleApplication1 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     var myVar = new List<parent >(); 
     myVar.Add(new parent() { id = "id1", blah1 = "blah1", c1 = new child() { blah2 = "blah2", blah3 = "blah3" } }); 
     myVar.Add(new parent() { id = "id1", blah1 = "blah1", c1 = new child() { blah2 = "blah2", blah3 = "blah3" } }); 

     var test = myVar.Distinct(); 

     Console.ReadKey(); 

    } 
} 


public class parent : IEquatable<parent> 
{ 
    public String id { get;set;} 
    public String blah1 { get; set; } 
    public child c1 { get; set; } 

    public override int GetHashCode() 
    { 
     unchecked // Overflow is fine, just wrap 
     { 
      int hash = 17; 
      // Suitable nullity checks etc, of course :) 
      hash = hash * 23 + id.GetHashCode(); 
      hash = hash * 23 + blah1.GetHashCode(); 
      hash = hash * 23 + (c1 == null ? 0 : c1.GetHashCode()); 
      return hash; 
     } 
    } 

    public bool Equals(parent other) 
    { 
     return object.Equals(id, other.id) && 
      object.Equals(blah1, other.blah1) && 
      object.Equals(c1, other.c1); 
    } 

} 

public class child : IEquatable<child> 
{ 
    public String blah2 { get; set; } 
    public String blah3 { get; set; } 

    public override int GetHashCode() 
    { 
     unchecked // Overflow is fine, just wrap 
     { 
      int hash = 17; 
      // Suitable nullity checks etc, of course :) 
      hash = hash * 23 + blah2.GetHashCode(); 
      hash = hash * 23 + blah3.GetHashCode(); 
      return hash; 
     } 
    } 

    public bool Equals(child other) 
    { 
     return object.Equals(blah2, other.blah2) && 
      object.Equals(blah3, other.blah3); 
    } 

} 
} 

qualcuno potrebbe individuare il mio errore (s)?

+4

+1 per un esempio completo. – SLaks

+1

Che errore ottieni? Qual è il comportamento previsto? L'esecuzione del codice non visualizza semplicemente nulla ed esce quando viene premuto un tasto. – jdmichal

+0

@jdmichal, inserisci un punto di interruzione su console.readkey e guarda la variabile di test, dovrebbe dire count = 1 not 2 – Fredou

risposta

5

È necessario eseguire l'override del metodo Equals(object):

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

Il metodo object.Equals (a differenza EqualityComparer<T>.Default) non utilizzare l'interfaccia IEquatable. Pertanto, quando scrivi object.Equals(c1, other.c1), non chiama il tuo metodo Child.Equals(Child).

Non è assolutamente necessario farlo per parent, ma è davvero dovrebbe.

+0

Errore 'ConsoleApplication1.parent. Uguale a (ConsoleApplication1.parent) ': non è stato trovato alcun metodo adatto per sovrascrivere – Fredou

+0

@Fredou: ho dimenticato di aggiungere il tipo di reso. – SLaks

+0

questo dovrebbe presupporre che c1! = Null e EqualityComparer non gli interesserebbe, giusto? – Fredou

0

Quando si aggiunge il calcolo del hash si potrebbe desiderare di provare qualcosa di simile

hash ^= id.GetHashCode();

Non sono sicuro se questo è ciò che sta causando il problema.

+0

Credo che il suo hashing sia valido - e in ogni caso, se fosse stato male porterebbe a prestazioni scadenti, non ad output non corretti (a patto che l'hash sia deterministico, che è chiaramente in questo caso). E il problema del perfezionamento non sarebbe certamente evidente con solo 2 elementi. :) –

3

o si fa quello che suggerisce SLaks, o si utilizza EqualityComparer<child>.Default nella classe parent di utilizzare il IEquatable<child> implementazione:

public bool Equals(parent other) 
    { 
    return object.Equals(id, other.id) && 
    object.Equals(blah1, other.blah1) && 
    EqualityComparer<child>.Default.Equals(c1, other.c1); 
} 
+0

questo lavoro, ora ho bisogno di sapere qual è la migliore pratica, implementando questo o il modo SLaks – Fredou

+1

Si ** veramente ** dovrebbe sovrascrivere 'Uguali (oggetto)'. Altrimenti, è probabile che tu abbia altre brutte sorprese. – SLaks

+0

deve eseguire l'override di Equals (oggetto): Accetto. – ulrichb

0

ci sono diverse cose per arrivare proprio qui. Se ho intenzione di implementare qualsiasi aspetto dell'uguaglianza in una classe come GetHashCode, ignorando == o IEquatable, utilizzo sempre il seguente schema.

  1. Override Equals
  2. Override GetHashCode
  3. Implementare IEquatable<T> che significa attuare Equals(T)
  4. Implementare! =
  5. Implementare ==

Quindi, se ho avuto una classe denominata ExpiryMonth con Proprietà Anno e Mese, ecco come apparirà questa implementazione. È un compito abbastanza insensato ora adattarsi ad altri tipi di classi.

Ho basato questo modello su diverse altre risposte StackOverflow che meritano tutto il merito, ma che non ho tracciato lungo il percorso.

Implementando sempre tutti questi elementi, garantisce operazioni di uguaglianza corrette in una varietà di contesti, inclusi i dizionari e le operazioni di Linq.

public static bool operator !=(ExpiryMonth em1, ExpiryMonth em2) 
    { 
     if (((object)em1) == null || ((object)em2) == null) 
     { 
      return !Object.Equals(em1, em2); 
     } 
     else 
     { 
      return !(em1.Equals(em2)); 
     } 
    } 
    public static bool operator ==(ExpiryMonth em1, ExpiryMonth em2) 
    { 
     if (((object)em1) == null || ((object)em2) == null) 
     { 
      return Object.Equals(em1, em2); 
     } 
     else 
     { 
      return em1.Equals(em2); 
     } 
    } 
    public bool Equals(ExpiryMonth other) 
    { 
     if (other == null) { return false; } 
     return Year == other.Year && Month == other.Month; 
    } 
    public override bool Equals(object obj) 
    { 
     if (obj == null) { return false; } 
     ExpiryMonth em = obj as ExpiryMonth; 
     if (em == null) { return false; } 
     else { return Equals(em); } 
    } 
    public override int GetHashCode() 
    { 
     unchecked // Overflow is not a problem 
     { 
      var result = 17; 
      result = (result * 397) + Year.GetHashCode(); 
      result = (result * 397) + Month.GetHashCode(); 
      return result; 
     } 
    }