问题 如何使用Linq Distinct方法的自定义比较器?


我正在读一本关于Linq的书,看到Distinct方法有一个需要比较器的重载。这对于我想要从集合中获取不同实体的问题是一个很好的解决方案,但是希望比较在实体ID上,即使其他属性不同。

根据这本书,如果我有一个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'

我已经搜索了一下,并且已经看到很多使用这样的代码的例子,但没有关于编译器错误的抱怨。

我究竟做错了什么?另外,这是最好的方法吗?我想要一个一次性解决方案,所以不要开始更改实体本身的代码。我希望实体保持正常,但只是在这一个地方,仅按ID进行比较。

谢谢你的帮助。


1852
2017-11-11 14:23


起源

忘了添加,我正在使用VS2012,所以在.NET中使用.NET 4.0 - Avrohom Yisroel


答案:


你将比较器实现为 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);

14
2017-11-11 14:31



谢谢彼得,这解释了!我在书中使用的例子是OrderBy,但是他从他讨论的区别提到了它,我没有意识到我需要一个不同的界面。实际上在书中不是很清楚:( - Avrohom Yisroel
顺便说一下,你知道是否有办法用匿名函数做到这一点,而不是必须为比较器创建一个单独的类?看起来像矫枉过正 - Avrohom Yisroel
据我所知,没有。我实际上一直在考虑用这样的东西重载linq运算符。 - Peter Davidsen
惭愧,因为对于像我这样的一次性情况,创造一个全新的课程是一种过度的伎俩。我习惯于抛出匿名函数!再次感谢。 - Avrohom Yisroel
我认为值得一提的是写一个自定义 IEqualityComparer 还需要实施 GetHashCode() - Elliot Starks