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); 

这组,但仍然有重复。

  1. 如何指定自定义代码来比较GroupBy字段? 例如,可以通过Component.Code比较组件。

这里的问题是你的密钥类型是匿名的,这意味着你不能声明为该密钥类型实现IEqualityComparer的类。 虽然可以编写一个比较器,以自定义方式比较匿名类型的相等性(通过generics方法,委托和类型推断),但它不会非常令人愉快。

两个最简单的选项可能是:

  • 通过覆盖PeriodEndDtoComponentDto Equals / GetHashCode,使匿名类型“正常工作”。 如果你想在任何地方使用自然平等,这可能是最安全的选择。 我建议也实现IEquatable
  • 不要使用匿名类型进行分组 – 使用命名类型,然后您可以覆盖GetHashCodeEquals ,或者您可以以正常方式编写自定义相等比较器。

编辑: ProjectionEqualityComparer不会真正起作用。 写一些类似的东西是可行的 – 一种CompositeEqualityComparer ,它允许你从几个“投影+比较器”对创建一个相等比较器。 然而,与匿名类型相比,它会非常难看。

编辑:

正如Jon Skeet指出的那样,这个解决方案似乎比它更好,如果你不太考虑它,因为我忘了实现GetHashCode。 正如乔恩在他的回答中所说,必须实施GetHashCode才能实现这种方法,“并不是非常令人愉快。” 据推测,这也是对框架中EqualityComparer.Create()所谓的“莫名其妙”)的解释。 我将留下答案作为参考,作为不该做的例子,也可以是有益的。

原始答案:

您可以使用.NET 4.5中引入的Comparer.Create模式建议的方法(但在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)