2010-02-17 6 views
11

J'ai cette classeComment regrouper par types personnalisés avec LINQ

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

Avec coordonnées étant définir comme ceci:

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

Et je veux avoir une requête LINQ comme ça:

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

As mentioned here by MSDN Je pensais que c'était possible si je devais remplacer Equals sur ma classe de coordonnées:

Utilisez un type nommé si vous devez transmettre la variable de requête à une autre méthode. Créez une classe spéciale à l'aide des propriétés implémentées automatiquement pour les clés , puis remplacez les méthodes Equals et GetHashCode. Vous pouvez également utiliser une struct, , auquel cas vous n'avez pas besoin de surcharger les méthodes . Pour plus d'informations, voir Comment : implémenter une classe Immuable Ce a des propriétés Mis en œuvre automatique

Equals mise en œuvre pour coordonner la classe:

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

encore je ne peux pas obtenir ma requête LINQ to groupe par des coordonnées similaires , comme mon défaut test de montre:

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

Il y a une surcharge la méthode GrouBy qui prend un IEqualityComparer en paramètre, mais y a-t-il l'équivalent en utilisant la clause group? Est-ce que je fais quelque chose de mal ?? Des pensées?

Répondre

22

Vous avez montré l'implémentation Equals, mais pas GetHashCode. Vous devez remplacer les deux (et de manière cohérente) pour que le regroupement fonctionne.

Exemple mise en œuvre GetHashCode:

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

Notez que la comparaison float valeurs pour l'égalité exacte est toujours un peu risqué - mais je au moins attends vos tests unitaires à passer, étant donné qu'ils ne sont pas effectuer de calculs .

+0

juste essayé, c'est ce que je manquait.Grand merci :) public override int GetHashCode() { return ((int) Latitude * 100)^((int) Longitude * 100); } –

+0

Si c'est ce que fait votre code de hachage, vous devez vous assurer que votre code d'égalité correspond à celui-ci - ils doivent être cohérents les uns avec les autres. –

+0

Latitude.GetHashCode()^Longitude.GetHashCode() donne les mêmes résultats lors de l'inversion de la latitude et de la longitude. Donc ce n'était pas une bonne solution car je veux m'assurer que coord (x, y)! = Coord (y, x); Votre code fonctionne puisque l'ordre des opérations est important. Merci pour la précision, qui a aidé :) –

2

Il y a une surcharge de la méthode GrouBy qui prend un IEqualityComparer comme paramètre, mais est-il l'équivalent en utilisant la clause de groupe?

Vous pouvez toujours groupe par un type anonyme, si vous voulez juste une solution en ligne rapide et ne sont pas inquiets au sujet de frapper le type exact pour les clés:

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

Je connaissais cette solution, mais vous n'aurez pas la classe de coordonnées comme clé, juste un type anonyme. –

Questions connexes