LINQ Group通过多个ref-type字段; 自定义EqualityComparer
所以我在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字段? 例如,可以通过Component.Code比较组件。
这里的问题是你的密钥类型是匿名的,这意味着你不能声明为该密钥类型实现IEqualityComparer
的类。 虽然可以编写一个比较器,以自定义方式比较匿名类型的相等性(通过generics方法,委托和类型推断),但它不会非常令人愉快。
两个最简单的选项可能是:
- 通过覆盖
PeriodEndDto
和ComponentDto
Equals / GetHashCode,使匿名类型“正常工作”。 如果你想在任何地方使用自然平等,这可能是最安全的选择。 我建议也实现IEquatable
- 不要使用匿名类型进行分组 – 使用命名类型,然后您可以覆盖
GetHashCode
和Equals
,或者您可以以正常方式编写自定义相等比较器。
编辑: ProjectionEqualityComparer
不会真正起作用。 写一些类似的东西是可行的 – 一种CompositeEqualityComparer
,它允许你从几个“投影+比较器”对创建一个相等比较器。 然而,与匿名类型相比,它会非常难看。
编辑:
正如Jon Skeet指出的那样,这个解决方案似乎比它更好,如果你不太考虑它,因为我忘了实现GetHashCode。 正如乔恩在他的回答中所说,必须实施GetHashCode才能实现这种方法,“并不是非常令人愉快。” 据推测,这也是对框架中EqualityComparer
所谓的“莫名其妙”)的解释。 我将留下答案作为参考,作为不该做的例子,也可以是有益的。
原始答案:
您可以使用.NET 4.5中引入的Comparer
模式建议的方法(但在EqualityComparer
莫名其妙地不存在)。 为此,请创建DelegateEqualityComparer
类:
class DelegateEqualityComparer : EqualityComparer { private readonly Func _equalityComparison; private DelegateEqualityComparer(Func 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 Create( Func equalityComparison) { return new DelegateEqualityComparer (equalityComparison); } }
然后围绕GroupBy方法编写包装器以接受Func
委托来代替IEqualityComparer
参数。 这些方法将委托包装在DelegateEqualityComparer
实例中,并将其传递给相应的GroupBy方法。 例:
public static class EnumerableExt { public static IEnumerable> GroupBy( this IEnumerable source, Func keySelector, Func equalityComparison) { return source.GroupBy( keySelector, DelegateEqualityComparer.Create(equalityComparison); } }
最后,在您的调用站点,您将使用此表达式之类的equalityComparison
参数:
(a, b) => a.PeriodEnd.Equals(b.PeriodEnd) && a.Component.Code.Equals(b.Component.Code) && a.GroupCode.Equals(b.GroupCode)