【问题标题】:Why does Linq not provide a lambda parameter instead of IEqualityComparer<T>?为什么 Linq 不提供 lambda 参数而不是 IEqualityComparer<T>?
【发布时间】:2015-05-04 19:37:54
【问题描述】:

因此,在 Linq 中的 GroupBy() 之类的东西中,您可以提供 IEqualityComparer&lt;T&gt; 的实现来帮助进行对象比较。不过,似乎简单地传入一个 lambda 表达式会更容易。

例子:

// current implementation
myCollection.GroupBy(c => c.Foo, c => c.Bar, new FooBarComparer());

// it seems easier to...
myCollection.GroupBy(c => c.Foo, c => c.Bar, (x, y) => x.Baz == y.Baz);

给定IEqualityComparer&lt;T&gt; 的简单实现,如下所示:

public class FooBarComparer : IEqualityComparer<FooBar> {
    public bool Equals(FooBar x, FooBar y) {
        return x.Baz == y.Baz;
    }

    public int GetHashCode(FooBar obj) {
        return obj.GetHashCode();
    }
}

似乎提供 lambda 表达式可能同样有效。就目前而言,如果我尝试将带有 Linq 查询的 IEqualityComparer&lt;T&gt; 传递给数据库,它会失败,因为 SQL Server(或其他)对我的类一无所知。似乎可以将 lambda 转换为可在目标数据库中使用的 SQL。

是否有特定原因在 Linq 中未将其作为选项提供?

【问题讨论】:

  • 一个 lambda 表达式不支持 GetHashCode,它在内部用于提高性能。
  • 您必须有第二个 lambda 来生成哈希码,但由于确定相等性和哈希码应该相关,因此将它们封装在同一个类中更有意义。
  • GetHashCode() 优先于Equals() 使用?我在想 lambda 的重载会使用该 lambda 进行比较,就好像它是 Equals()
  • 如果你愿意,可以直接按c.Foo.Baz分组,
  • 我投票结束这个问题,因为它需要阅读该功能开发人员的想法。

标签: c# linq lambda


【解决方案1】:

您需要两个 lambda,以便 GetHashCode 也有一个等价物。除此之外,这会起作用,是的。有一些 LINQ 方法不使用哈希码但使用相等 (Enumerable.Contains)。

我想这只是有一个标准 API 用于整个 BCL 使用的相等性。通过使用委托支持的比较器实现或将myComparer.Equals 转换为委托,您可以轻松地在委托和比较器之间进行转换。

将表达式远程发送到数据库并不容易远程比较器表达式。 GROUP BY 在 SQL 中不支持。它肯定可以工作,但它是一个小众用例(实际上,如果 GroupBy 的比较器表达式不提供相等关系,我不确定转换为 SQL 时会如何)。

【讨论】:

    【解决方案2】:

    要制作高效的GroupBy/Distinct,您需要两件事:

    • 相等比较器
    • 哈希生成器,用于创建哈希字典

    或者你可以遵循 C++ 路线

    • 能够对元素进行排序的比较器,以便您能够创建树

    如果你只有一个相等比较器,那么做GroupBy 的难度类似于O(n^2),因为如果你有 5 个元素,你需要 5 + 4 + 3 + 2 + 1 比较,所以 n * (n + 1) / 2 所以 15。这是一个“好”的库永远不允许你做的事情(而且没有一个理智的 SQL 服务器会做!)

    现在,很明显 LINQ 库可以分析你的相等 lambda,看看它是

    (x, y) => x.Baz == y.Baz
    

    看到它是对称的,所以左项和右项是形式

    x => x.Baz
    

    并使用它来生成一个哈希器并选择一个比较器。但是到了这个地步,直接做不是更容易吗

    myCollection.GroupBy(c => c.Foo.Baz) 
    

    是的,你可以这样做:-)

    然后,

    myCollection.GroupBy(c => c.Foo.Baz, c => new { c.Foo, c.Bar })
                .Select(c => new { Key = c.First().Foo, Values = c.Select(x => x.Bar) })
    

    这与您的预期 GroupBy 非常相似(唯一的区别是值在 Values IEnumerable&lt;&gt; 中)

    现在...对于使用 IEqualityComparer&lt;T&gt;... 的重载,正如我在 cmets 中所写的那样,我认为它们应该与 LINQ 提供程序可以识别的“股票”比较器一起使用,比如各种StringComparer.*(例如StringComparer.OrdinalIgnoreCase)和EqualityComparer&lt;T&gt;.Default,代表“默认”比较器。

    【讨论】:

    • C# 和 C++ 都支持相等比较器和排序比较器。它们并不特定于一种语言或另一种语言。在 C# 中,这将是 IComparerIEqualityComparer。我不确定它们在 C++ 中分别映射到什么,但我相信它们都以某种形式存在。
    • @Servy 如果您查看 C++ 的 map/multimap,您会发现它基于 operator&lt;(或其他排序运算符)(例如参见 stackoverflow.com/questions/6573225/…) .这是因为 C++ 没有像 C# 那样的哈希码的“原始”概念
    • 您列出了两个不基于散列的数据结构这一事实并不意味着 C++ 没有任何基于散列的数据结构。这就像说因为SortedDictionary 不是基于散列的C# 不使用基于散列的数据结构。两种语言都支持(即使每种语言都让不同的语言“更容易”)。
    • @Servy 多年来,C++ 没有unordered_map。它仅在 C++11 中引入。所以当我说“或者你可以遵循 C++ 路线”时,我是对的:多年来,C++ 路线是“我们没有散列函数,我们使用比较器”。近年来,它已变为“我们都有”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 2015-01-02
    • 2011-05-12
    • 2012-09-04
    • 2020-02-10
    • 2020-09-19
    相关资源
    最近更新 更多