重载 IEqualityComparer 的 GroupJoin 只比较内部集合中的对象

GroupJoin overloaded with IEqualityComparer only compares objects in the inner collection

我在使用客户 IEqualityComparer 实施组加入时遇到了一些奇怪的行为。

以下代码演示了对我来说是问题的行为

List<String> inner = new List<string>() { "i1", "i2" };
List<String> outer = new List<string>() { "o1", "o2" };

var grouped = outer.GroupJoin(inner, i => i, o=> o, (inKey, outCollection) => new {Key = inKey, List = outCollection},
        new EqualityComparer<string>((i, o) => i == o)).ToList();

根据 MSDN 上的文档,我希望最后一个参数传递一系列内键和外键以进行比较。

然而,在 Func 中放置一个断点表明 i 和 o 都以字母 i 开头,并且实际上都是内部集合的元素,因此 grouped 对象始终为空(我知道这个例子将始终为空,它只是演示问题的最小代码。

有没有办法使用自定义比较器来加入对象?

为了完整起见,这是在 GroupJoin 参数列表中创建的 EqualityComparer:

public class EqualityComparer<T> : IEqualityComparer<T>
{
    public EqualityComparer(Func<T, T, bool> cmp)
    {
        this.cmp = cmp;
    }
    public bool Equals(T x, T y)
    {
        return cmp(x, y);
    }

    public int GetHashCode(T obj)
    {
        // Always return 0 so that the function is called
        return 0;
    }

    public Func<T, T, bool> cmp { get; set; }
}

A GroupJoin 操作首先需要构建一个查找 - 基本上是从 inner 中的每个投影键到具有该键的 inner 的元素。这就是您被传递 inner 值的原因。这种情况在 "when the first result is requested" 方面是延迟发生的,但此时它会消耗整个 inner

然后,一旦构建了查找,outer 就会流式传输,一次一个元素。此时,应该要求您的自定义相等比较器比较内部键和外部键。事实上,当我将日志记录添加到您的比较器(我已重命名以避免与框架 EqualityComparer<T> 类型发生冲突)时,我看到:

using System;
using System.Linq;
using System.Collections.Generic;

public class Test
{
    public static void Main()
    {
        List<String> inner = new List<string>() { "i1", "i2" };
        List<String> outer = new List<string>() { "o1", "o2" };

        outer.GroupJoin(inner, i => i, o=> o,
            (inKey, outCollection) => new {Key = inKey, List = outCollection},
            new CustomEqualityComparer<string>((i, o) => i == o)).ToList();
    }
}

public class CustomEqualityComparer<T> : IEqualityComparer<T>
{
    public CustomEqualityComparer(Func<T, T, bool> cmp)
    {
        this.cmp = cmp;
    }
    public bool Equals(T x, T y)
    {
        Console.WriteLine("Comparing {0} and {1}", x, y);
        return cmp(x, y);
    }

    public int GetHashCode(T obj)
    {
        // Always return 0 so that the function is called
        return 0;
    }

    public Func<T, T, bool> cmp { get; set; }
}

输出:

Comparing i1 and i2
Comparing i1 and i2
Comparing i1 and i2
Comparing i2 and o1
Comparing i1 and o1
Comparing i2 and o2
Comparing i1 and o2

现在这不是 唯一 可能的 GroupJoin 实现,但它是一个相当明显的实现。有关详细信息,请参阅 GroupJoin 上我的 Edulinq post