2010-02-17 1 views
11

ho questa classeCome gruppo per i tipi personalizzati con LINQ

public class Item 
{ 
     public Coordinate coordinate { get; set; } 
     ... 
     ... 
} 

Con coordinate in fase di definire in questo modo:

public class Coordinate 
{ 
     public Coordinate(float latitude, float longitude) 
     { 
      Latitude = latitude; 
      Longitude = longitude; 
     } 

     public float Latitude { get; private set; } 
     public float Longitude { get; private set; } 
} 

E voglio avere una query LINQ come quella:

var grouped = from it in items 
       group it by it.Coordinate into grp 
       select grp; 

As mentioned here by MSDN Ho pensato che sarebbe stato possibile se avessi eseguito l'override di Equals sulla mia classe Coordinate:

Utilizzare un tipo denominato se è necessario passare la variabile di query a un altro metodo. Creare una classe speciale utilizzando le proprietà implementate automaticamente per le chiavi e quindi sovrascrivere i metodi Uguale e GetHashCode. È inoltre possibile utilizzare una struttura, nel qual caso non è necessario sostituire i metodi con i metodi . Per ulteriori informazioni, vedere Come : implementare una classe immutabile, che ha proprietà Auto-Implemented

Uguale implementazione per Coordinate classe:

public override bool Equals(object obj) 
{ 
     var coord = obj as Coordinate; 
     if(coord == null) return false; 
     return (Latitude == coord.Latitude && Longitude == coord.Longitude); 
} 

ancora non posso ottenere la mia query LINQ per raggruppare da coordinate simili , come il mio mancanza test mostra:

[TestMethod] 
public void GroupBy_3ItemsWith2DifferentCoordinates_Returns2Groups() 
{ 
    var items = new List<Item> 
     { 
      new Item {Coordinate = new Coordinate(10, 10)}, 
      new Item {Coordinate = new Coordinate(10, 10)}, 
      new Item {Coordinate = new Coordinate(12, 10)}, 
     }; 
    var grouped = from it in items 
        group it by it.Coordinate into g 
        select g; 
    Assert.AreEqual(2, grouped.Count()); 
} 

C'è un sovraccarico di il metodo GrouBy che prende come parametro un IEqualityComparer, ma esiste l'equivalente usando la clausola group? Sto facendo qualcosa di sbagliato ?? qualche idea?

risposta

22

Hai mostrato l'implementazione Equals, ma non GetHashCode. È necessario eseguire l'override di entrambi (e in modo coerente) affinché il raggruppamento funzioni.

Esempio implementazione GetHashCode:

public override int GetHashCode() 
{ 
    int hash = 23; 
    hash = hash * 31 + Latitude.GetHashCode(); 
    hash = hash * 31 + Longitude.GetHashCode(); 
    return hash; 
} 

Nota che il confronto float valori di uguaglianza esatta è sempre un po 'rischioso - ma mi aspetto che almeno il test di unità di passare, dato che non stanno eseguendo calcoli .

+0

Appena provato, questo è quello che mancava.Grazie mille :) override pubblico int GetHashCode() { return ((int) Latitude * 100)^((int) Longitude * 100); } –

+0

Se è quello che sta facendo il tuo codice hash, dovresti assicurarti che il tuo codice di uguaglianza lo corrisponda - dovrebbero essere coerenti tra loro. –

+0

Latitude.GetHashCode()^Longitude.GetHashCode() restituisce gli stessi risultati quando si invertono Latitudine e Longitudine. Quindi non è stata una buona soluzione in quanto voglio garantire che coord (x, y)! = Coord (y, x); Il tuo codice funziona poiché l'ordine delle operazioni è importante. Grazie per la precisione, mi ha aiutato :) –

2

C'è un sovraccarico al metodo GrouBy che accetta un IEqualityComparer come parametro, ma c'è la equivalente utilizzando la clausola gruppo?

Puoi sempre di gruppo da parte di un tipo anonimo, se si desidera solo una soluzione in linea veloce e non sono preoccupati per colpire il tipo esatto per le chiavi:

var grouped = 
    from it in items 
    group it by new {it.Coordinate.Latitude, it.Coordinate.Longitude}; 
+0

Sapevo che la soluzione, ma non avresti la classe di coordinate come chiave, solo un tipo anonimo. –