使用IEqualityComparer重载的GroupJoin仅比较内部集合中的对象

时间:2015-11-04 12:32:28

标签: c# lambda iequalitycomparer

在与客户IEqualityComparer实施Group Join时遇到了一些奇怪的行为。

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

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对象使用自定义比较器?

为了完整性,这是在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; }
}

1 个答案:

答案 0 :(得分:2)

GroupJoin操作首先需要构建一个查找 - 基本上是从inner中的每个投影键到具有该键的inner元素。这就是你被传递inner值的原因。这种情况在“当请求第一个结果时”时会延迟发生,但此时它会消耗整个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