如何使用linq按自定义类型进行分组

时间:2010-02-17 11:29:34

标签: .net linq group-by

我有这个班级

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

坐标的定义如下:

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

我希望得到这样的linq查询:

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

As mentioned here by MSDN我认为如果我在我的Coordinate类上重写Equals是可能的:

  

如果必须通过,请使用命名类型   查询变量到另一个方法。   使用创建一个特殊的类   自动实现的属性   键,然后覆盖等于   和GetHashCode方法。你也可以   使用结构,在这种情况下你不使用   严格来说必须覆盖那些   方法。有关更多信息,请参阅如何   to:实现一个不可变类   具有自动实现的属性

Coordinate类的等于实现:

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

仍然无法通过类似的坐标将我的linq查询分组,因为我的失败的测试显示:

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

GrouBy方法有一个重载,它将IEqualityComparer作为参数,但使用group子句是否存在等价物? 难道我做错了什么??有什么想法吗?

2 个答案:

答案 0 :(得分:23)

您已展示了Equals实现,但未展示GetHashCode。您需要覆盖(并以一致的方式)分组才能工作。

示例GetHashCode实现:

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

请注意,比较float值以确保完全相等总是有些冒险 - 但我至少期望您的单元测试通过,因为它们没有执行任何计算。

答案 1 :(得分:2)

  

GrouBy过载   采用IEqualityComparer的方法   作为参数,但是有   相当于使用group子句?

如果您只想要一个快速的内联解决方案并且不担心按键的确切类型,您可以始终按匿名类型进行分组:

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