2

因此,我在 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 进行比较。
4

2 回答 2

8

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

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

  • PeriodEndDto通过在and中覆盖 Equals/GetHashCode 使匿名类型“正常工作” ComponentDto。如果您想在任何地方使用自然平等,这可能是最明智的选择。我IEquatable<T>也建议实施
  • 不要使用匿名类型进行分组 - 使用命名类型,然后您可以覆盖它GetHashCodeEquals或者您可以以正常方式编写自定义相等比较器。

编辑:ProjectionEqualityComparer不会真的工作。不过,编写类似的东西是可行的——CompositeEqualityComparer其中一种允许您从几个“投影+比较器”对创建一个相等比较器。与匿名类型相比,它会非常难看。

于 2012-10-03T06:48:20.053 回答
3

编辑:

正如 Jon Skeet 指出的那样,这个解决方案似乎比它更好,如果你不仔细考虑的话,因为我忘记了实现 GetHashCode。正如 Jon 在他的回答中所说,必须实现 GetHashCode 使得这种方法“不是非常令人愉快”。EqualityComparer<T>.Create()想必,这也是框架中(所谓的“莫名其妙”)缺席的解释。我将把答案留作参考,作为不该做的例子,也可能具有指导意义。

原始答案:

您可以使用Comparer<T>.Create.NET 4.5 中引入的模式建议的方法(但在 .NET 中莫名其妙地不存在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)
于 2012-10-03T08:14:34.380 回答