多个ref类型字段上的LINQ GroupBy;自定义EqualityComparer
-
13-12-2019 - |
题
所以我已经在SO和其他地方看了大约20个例子,但没有找到一个涵盖我正在尝试做的事情。这 - 我可以内联指定我的显式类型比较器吗? -看起来像我需要的,但没有走得足够远(或者我不明白如何进一步)。
- 我有一个LoadData列表,LoadData对象具有引用和值类型的字段
- 需要对ref和value字段的混合进行分组,将输出投影到匿名类型
需要(我认为)提供一个自定义的IEqualityComparer来指定如何比较GroupBy字段,但它们是匿名类型
private class LoadData { public PeriodEndDto PeriodEnd { get; set; } public ComponentDto Component { get; set; } public string GroupCode { get; set; } public string PortfolioCode { get; set; } }
最好的GroupBy查询我有去到目前为止:
var distinctLoads = list.GroupBy(
dl => new { PeriodEnd = dl.PeriodEnd,
Component = dl.Component,
GroupCode = dl.GroupCode },
(key, data) => new {PeriodEnd = key.PeriodEnd,
Component = key.Component,
GroupCode = key.GroupCode,
PortfolioList = data.Select(d=>d.PortfolioCode)
.Aggregate((g1, g2) => g1 + "," + g2)},
null);
此组,但仍有重复。
- 如何指定自定义代码来比较GroupBy字段?例如,组分可以按组分进行比较。密码。
解决方案
这里的问题是你的键类型是匿名的,这意味着你不能声明一个实现的类 IEqualityComparer<T>
为该密钥类型。虽然会是 可能的 要编写一个比较器,以自定义方式(通过泛型方法,委托和类型推断)比较匿名类型的相等性,它不会非常愉快。
最简单的两个选项可能是:
- 通过重写Equals/GetHashCode使匿名类型"just work"in
PeriodEndDto
和ComponentDto
.如果你想在任何地方使用自然平等,这可能是最明智的选择。我建议实施IEquatable<T>
也一样 - 不要使用匿名类型进行分组-使用命名类型,然后您可以复盖
GetHashCode
和Equals
在这一点上,或者你可以用正常的方式编写一个自定义相等比较器。
编辑: ProjectionEqualityComparer
真的行不通。写一些类似的东西是可行的--一种 CompositeEqualityComparer
这允许您从几个"投影+比较器"对创建相等比较器。与匿名类型相比,这将是非常丑陋的。
其他提示
编辑:
正如Jon Skeet所指出的那样,这个解决方案似乎比它更好,如果你不仔细考虑它,因为我忘记了实现GetHashCode。必须实现GetHashCode使得这种方法,正如Jon在他的回答中所说,"不是非常愉快。"据推测,这也是对(所谓的"莫名其妙的")缺席的解释。 EqualityComparer<T>.Create()
框架中。我会留下答案供参考,作为不做什么的例子,也可以是有益的。
原答案:
你可以使用 Comparer<T>.Create
在.NET4.5中引入的模式(但在 EqualityComparer<T>
).为此,创建一个 DelegateEqualityComparer<T>
班级:
class DelegateEqualityComparer<T> : EqualityComparer<T>
{
private readonly Func<T, T, bool> _equalityComparison;
private DelegateEqualityComparer(Func<T, T, bool> equalityComparison)
{
if (equalityComparison == null)
throw new ArgumentNullException("equalityComparison");
_equalityComparison = equalityComparison;
}
public override bool Equals(T x, T y)
{
return _equalityComparison(x, y);
}
public static DelegateEqualityComparer<T> Create(
Func<T, T, bool> equalityComparison)
{
return new DelegateEqualityComparer<T>(equalityComparison);
}
}
然后在GroupBy方法周围编写包装器以接受 Func<TKey, TKey, bool>
代表代替 IEqualityComparer<TKey>
参数。这些方法将委托包装在 DelegateEqualityComparer<T>
实例,并将其传递给相应的GroupBy方法。例子::
public static class EnumerableExt
{
public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TKey, TKey, bool> equalityComparison)
{
return source.GroupBy(
keySelector,
DelegateEqualityComparer<TKey>.Create(equalityComparison);
}
}
最后,在您的呼叫站点,您将使用类似于此表达式的 equalityComparison
论点:
(a, b) => a.PeriodEnd.Equals(b.PeriodEnd)
&& a.Component.Code.Equals(b.Component.Code)
&& a.GroupCode.Equals(b.GroupCode)