Как группировать пользовательские типы с помощью linq

У меня этот класс

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;

Как уже упоминалось здесь по MSDN, я думал, что это возможно, если я переопределю Equals в моем классе Coordinate:

Используйте именованный тип, если вы должны передать переменная запроса к другому методу. Создайте специальный класс, используя автоматически реализованные свойства для и затем переопределить равные и GetHashCode. Вы также можете используйте структуру, и в этом случае вы не строго должны переопределять эти методы. Для получения дополнительной информации см. Раздел: Внедрить неизменяемый класс, который Имеет автоматически реализованные свойства

Выполнение равенства для класса 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? Я делаю что-то неправильно?? Любые мысли?

Ответ 1

Вы показали реализацию Equals, но не GetHashCode. Вам необходимо переопределить оба (и последовательно) для группировки для работы.

Пример реализации GetHashCode:

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

Обратите внимание, что сравнение значений float для точного равенства всегда несколько рискованно - но я бы хотя бы ожидал, что ваши юнит-тесты пройдут, учитывая, что они не выполняют никаких вычислений.

Ответ 2

Существует перегрузка GrouBy метод, который принимает IEqualityComparer как параметр, но есть ли эквивалентно с помощью предложения группы?

Вы всегда можете группировать анонимным типом, если вы просто хотите быстрое встроенное решение и не беспокоитесь о том, чтобы нажать точный тип ключей:

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