2009-11-06 12 views
13

Sto provando a fare un Gruppo Linq su alcuni oggetti utilizzando un tipo di chiave esplicito. Non sto passando un IEqualityComparer al GroupBy, quindi in base ai documenti: l'uguaglianza di confrontoConfronto chiave per Linq GroupBy utilizzando EqualityComparer predefinito

L'impostazione predefinita Default viene utilizzato per confrontare le chiavi.

E spiega la proprietà EqualityComparer<T>.Default in questo modo:

I Default controlli di proprietà se tipo T implementa l'interfaccia generica System.IEquatable<T> e in tal caso restituisce un EqualityComparer<T> che utilizza tale implementazione.

Nel seguente codice, sto raggruppando un array di oggetti Fred. Hanno un tipo di chiave chiamato FredKey, che implementa IEquatable<FredKey>.

Questo dovrebbe essere sufficiente per far funzionare il raggruppamento, ma il raggruppamento non funziona. Nell'ultima riga qui sotto dovrei avere 2 gruppi, ma io no, ho solo 3 gruppi contenenti i 3 elementi di input.

Perché è il raggruppamento non funziona?

class Fred 
{ 
    public string A; 
    public string B; 
    public FredKey Key 
    { 
     get { return new FredKey() { A = this.A }; } 
    } 
} 

class FredKey : IEquatable<FredKey> 
{ 
    public string A; 
    public bool Equals(FredKey other) 
    { 
     return A == other.A; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var f = new Fred[] 
     { 
      new Fred {A = "hello", B = "frog"}, 
      new Fred {A = "jim", B = "jog"}, 
      new Fred {A = "hello", B = "bog"}, 
     }; 

     var groups = f.GroupBy(x => x.Key); 
     Debug.Assert(groups.Count() == 2); // <--- fails 
    } 
} 

risposta

15

Da MSDN

Se si implementa IEquatable, si dovrebbe anche ignorare le implementazioni della classe base dell'oggetto :: Equals (Object) e GetHashCode() in modo che il loro comportamento è coerente con quella del Metodo IEquatable :: Equals. Se si esegue l'override di Object :: Equals (Object), l'implementazione sovrascritta viene anche chiamata nelle chiamate al metodo Equals statico (System.Object, System.Object) sulla classe. Ciò garantisce che tutte le invocazioni del metodo Equals() restituiscano risultati coerenti.

aggiungere questo al FredKey e dovrebbe funzionare

public override int GetHashCode() 
    { 
     return A.GetHashCode(); 
    } 
+0

È stato. Grazie. Ho trovato una buona spiegazione qui: http://www.codeproject.com/KB/dotnet/IEquatable.aspx –

+0

Questo significa che dovresti avere * due * Uguali metodi in 'FredKey': uno per' Equals (Object) ' e uno per 'Equals (FredKey)'? – bonh

5

Ecco un esempio completo with a Fiddle. Nota: l'esempio è diverso da leggermente dall'esempio della domanda.

La seguente implementazione di IEquatable può fungere da TKey in GroupBy. Si noti che include sia GetHashCode e Equals.

public class CustomKey : IEquatable<CustomKey> 
{ 
    public string A { get; set; } 
    public string B { get; set; } 

    public bool Equals(CustomKey other) 
    { 
     return other.A == A && other.B == B; 
    } 

    public override int GetHashCode() 
    { 
     return string.Format("{0}{1}", A, B).GetHashCode(); 
    } 
} 

public class Custom 
{ 
    public string A { get; set; } 
    public string B { get; set; } 
    public string C { get; set; } 
} 

public static void Main() 
{ 
    var c = new Custom[] 
     { 
      new Custom {A = "hello", B = "frog" }, 
      new Custom {A = "jim", B = "jog" }, 
      new Custom {A = "hello", B = "frog" }, 
     }; 

    var groups = c.GroupBy(x => new CustomKey { A = x.A, B = x.B }); 
     Console.WriteLine(groups.Count() == 2); 
} 
Problemi correlati