2

だから私はこれについてSOや他の場所で約20の例を見てきましたが、私がやろうとしていることをカバーするものは見つかりませんでした. これ -明示的な型コンパレータをインラインで指定できますか? -必要なもののように見えますが、十分ではありません (または、それをさらに進める方法がわかりません)。

  • LoadData のリストがあり、LoadData オブジェクトには参照型と値型の両方のフィールドがあります
  • 参照フィールドと値フィールドの混合物をグループ化し、出力を匿名型に射影する必要がある
  • GroupBy フィールドを比較する方法を指定するためにカスタム IEqualityComparer を提供する必要があります (私は思います)が、それらは匿名型です

    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>。つまり、そのキーの型を実装するクラスを宣言することはできません。独自の方法で (ジェネリック メソッド、デリゲート、および型推論を介して) 匿名型の等価性を比較するコンパレータを作成することは可能ですが、それほど快適ではありません。

最も単純な 2 つのオプションは、おそらく次のとおりです。

  • PeriodEndDtoおよびで Equals/GetHashCode をオーバーライドすることにより、匿名型を「正常に機能する」ようにしますComponentDto。どこでも使用したい自然な平等がある場合、これはおそらく最も健全なオプションです。私も導入をお勧めIEquatable<T>します
  • GetHashCodeグループ化に匿名型を使用しないでください。名前付き型を使用すると、その上で上書きするかEquals、通常の方法でカスタム等値比較子を作成できます。

編集:ProjectionEqualityComparer実際には機能しません。ただし、似たようなものを書くことは可能CompositeEqualityComparerです。これにより、いくつかの「射影 + 比較子」のペアから等値比較子を作成できるようになります。ただし、匿名型と比較するとかなり醜いでしょう。

于 2012-10-03T06:48:20.053 に答える
3

編集:

Jon Skeet が指摘しているように、この解決策は、よく考えなければ実際よりも優れているように思えます。GetHashCode を実装しなければならないことが、このアプローチを可能にします。Jon が彼の回答で述べているように、「ひどく快適ではありません」。おそらく、これはフレームワークに が存在しない (いわゆる「不可解」) ことの説明でもありEqualityComparer<T>.Create()ます。してはいけないことの例として、参考のために答えを残しておきます。

元の答え:

.NET 4.5 で導入されたパターンによって提案されたアプローチを使用できますComparer<T>.Create(ただし、.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 に答える