Как использовать настраиваемый компаратор с методом Linq Distinct?

Я читал книгу о Linq и увидел, что метод Distinct имеет перегрузку, требующую компаратора. Это было бы хорошим решением моей проблемы, когда я хочу получить отдельные сущности из коллекции, но хочу, чтобы сравнение производилось по идентификатору сущности, даже если другие свойства отличаются.

Согласно книге, если у меня есть сущность Gribulator, я смогу создать такой компаратор ...

private class GribulatorComparer : IComparer<Gribulator> {
  public int Compare(Gribulator g1, Gribulator g2) {
    return g1.ID.CompareTo(g2.ID);
  }
}

... а потом использовать вот так ...

List<Gribulator> distinctGribulators
  = myGribulators.Distinct(new GribulatorComparer()).ToList();

Однако это дает следующие ошибки компилятора ...

'System.Collections.Generic.List' не содержит определения для 'Distinct' и лучшей перегрузки метода расширения 'System.Linq.Enumerable.Distinct (System.Collections.Generic.IEnumerable, System.Collections.Generic.IEqualityComparer)' имеет некоторые недопустимые аргументы

Аргумент 2: невозможно преобразовать из LinqPlayground.Program.GribulatorComparer в System.Collections.Generic.IEqualityComparer

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

Что я делаю неправильно? Кроме того, это лучший способ сделать это? Я хочу здесь одноразовое решение, поэтому не хочу начинать изменять код самой сущности. Я хочу, чтобы сущность оставалась нормальной, но только в одном месте, сравнивайте только по идентификатору.

Спасибо за любую помощь.


person Avrohom Yisroel    schedule 11.11.2012    source источник
comment
Забыл добавить, я использую VS2012, и поэтому .NET 4.0 на C #   -  person Avrohom Yisroel    schedule 11.11.2012


Ответы (1)


Вы реализуете свой компаратор как IComparer<T>, перегрузка метода LINQ требует реализации IEqualityComparer:

private class GribulatorComparer : IEqualityComparer<Gribulator> {
  public bool Equals(Gribulator g1, Gribulator g2) {
    return g1.ID == g2.ID;
  }
}

редактировать:

Для пояснения: интерфейс IComparer может использоваться для сортировки, поскольку это в основном то, что делает метод Compare().

Нравится:

items.OrderBy(x => new ItemComparer());

private class ItemComparer : IComparer<Item>
{
    public int Compare(Item x, Item y)
    {
        return x.Id.CompareTo(y.Id)
    }
}

Что будет отсортировать вашу коллекцию с помощью этого компаратора, однако LINQ предоставляет способ сделать это для простых полей (например, int Id).

items.OrderBy(x => x.Id);
person Peter Davidsen    schedule 11.11.2012
comment
Спасибо, Питер, это все объясняет! Пример, который я использовал в книге, был для OrderBy, но он сослался на него, где обсуждал Distinct, и я не понял, что мне нужен другой интерфейс. На самом деле в книге было не очень ясно :( - person Avrohom Yisroel; 11.11.2012
comment
Кстати, знаете ли вы, есть ли способ сделать это с помощью анонимной функции, вместо того, чтобы создавать отдельный класс для компаратора? Похоже на излишество - person Avrohom Yisroel; 11.11.2012
comment
Насколько я знаю, нет. Я действительно думал о перегрузке операторов linq подобными вещами. - person Peter Davidsen; 12.11.2012
comment
Позор, потому что для такой разовой ситуации, как моя, создание совершенно нового класса - это излишне. Я привык разбрасывать анонимные функции! Еще раз спасибо. - person Avrohom Yisroel; 12.11.2012
comment
Думаю, стоит упомянуть, что для написания пользовательского IEqualityComparer также требуется реализация GetHashCode() - person Elliot Starks; 21.06.2017