GroupJoin, перегруженный IEqualityComparer, сравнивает только объекты во внутренней коллекции
Я столкнулся с некоторым странным поведением при реализации группового соединения с клиентом 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
объект всегда пуст (я знаю, что пример всегда будет пустым, это всего лишь маленький кусочек кода, который демонстрирует проблему).
Есть ли способ для объектов Group Join с пользовательским компаратором?
Для полноты, это EqualityComparer, который создается в списке аргументов Group Join:
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 ответ
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
, но это довольно очевидный. Смотрите мой пост Edulinq на GroupJoin
Больше подробностей.