Why does Linq not provide a lambda parameter instead of IEqualityComparer<t>?

时间:2015-05-04 19:37:54

标签: c# linq lambda

So in things like GroupBy() in Linq, you can provide an implementation of IEqualityComparer<T> to help with object comparisons. It seems, though, that it would be easier to simply pass in a lambda expression.

Example:

// 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);

Given a simple implementation of IEqualityComparer<T> like this:

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

It seems that providing a lambda expression could be just as effective. As it stands now, if I try to pass in an IEqualityComparer<T> with a Linq query to a database, it fails because SQL Server (or whatever) doesn't know anything about my class. It seems that a lambda would be able to be translated into SQL that can be used in the target database.

Is there a specific reason this is not provided as an option in Linq?

2 个答案:

答案 0 :(得分:0)

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

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

对于数据库的远程表达式,远程比较器表达式并不容易。 GROUP BY在SQL中不支持。它肯定可以工作,但它是一个利基用例(实际上,如果GroupBy的比较表达式不提供相等关系,我不确定在转换为SQL时会出现这种情况。)< / p>

答案 1 :(得分:0)

要提高效率GroupBy / Distinct,您需要做两件事:

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

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

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

如果你只有一个相等比较器,那么GroupBy的难度就像 O(n ^ 2),因为如果你有5个元素,你需要5 + 4 + 3 + 2 + 1比较,所以n *(n + 1)/ 2等15.这是一个好的&#34;图书馆不会允许你做(并且没有理智的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<>

现在......对于使用IEqualityComparer<T>的重载...正如我在评论中写的那样,我认为它们应该与&#34; stock&#34;比较器,LINQ提供程序可以识别,如各种StringComparer.*(例如StringComparer.OrdinalIgnoreCase)和EqualityComparer<T>.Default,代表&#34;默认&#34;比较器。