複数のREF型フィールドでLINQ GroupBy。カスタムQualityComparer
-
13-12-2019 - |
質問
だから私はこれについての約20の例をとてもそして他の場所で見ていましたが、私がやろうとしていることをカバーするものを見つけていません。これは - インラインで指定できますか? - 私が必要なもののように見えますが、それほど遠くない(または私はそれをさらにそれを取る方法を理解していません)。
- 私はloadDataのリストを持っています、loadDataオブジェクトには、参照タイプと値の型の両方のフィールドがあります
- REFフィールドと値のフィールドの混在をグループ化する必要がある場合は、出力を匿名型 に投影する必要があります。
-
groupbyフィールドの比較方法を指定するためのカスタムIEQUALITEComparerを提供する必要がありますが、それらは匿名型
です。private class LoadData { public PeriodEndDto PeriodEnd { get; set; } public ComponentDto Component { get; set; } public string GroupCode { get; set; } public string PortfolioCode { get; set; } }
最高のグループビークエリこれまでに進んでいます:
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<T>
を実装するクラスを宣言することはできません。 になるのは、(一般的な方法、代議員、および型推論を介して)カスタム方法で匿名のタイプを比較する比較器を書くことは、それはひどく心地よいものではないでしょう。
2つの最も簡単なオプションはおそらく:
-
PeriodEndDto
とComponentDto
のequals / gethashcodeをオーバーライドすることで、匿名のタイプ「Not Work」を作ります。どこでも使用したいという自然な平等がある場合、これはおそらく最高のオプションです。IEquatable<T>
を実装することをお勧めします - グループ化に匿名タイプを使用しない - 名前付きタイプを使用してから、それ以降の
GetHashCode
とEquals
をオーバーライドすることも、通常の方法でカスタムの平等比較器を書くこともできます。
編集:ProjectionEqualityComparer
は実際には機能しません。似たようなものを書くことは可能です - いくつかの「投影+比較器」ペアから平等比較器を作成することを可能にする一種のCompositeEqualityComparer
を書くことが可能であろう。それは匿名のタイプと比べてかなり醜いでしょう。
他のヒント
編集:
Jon SKEETが指摘しているように、この解決策はそれほど難しいとは思っていない場合は、GethashCodeを実装するのを忘れていたので、それよりも優れているようです。ジョンコードを実装する必要があるので、Jonは彼の答えで「ひどく快適ではない」と言うように、このアプローチを行います。おそらく、これはフレームワーク内のEqualityComparer<T>.Create()
の(いわゆる「不可能な」)存在についても説明である。何がそうでないかの例として、参考のために答えを残します。
オリジナルアンサー:
.NET 4.5で導入されたComparer<T>.Create
パターンによって提案されたアプローチを使用することができます(ただし、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メソッドの周囲にラッパーを書きます。これらのメソッドは、DelegateをFunc<TKey, TKey, bool>
インスタンスに囲み、それを対応する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);
}
}
.
最後に、あなたの通話サイトで、IEqualityComparer<TKey>
引数のこの式のようなものを使うでしょう:
(a, b) => a.PeriodEnd.Equals(b.PeriodEnd)
&& a.Component.Code.Equals(b.Component.Code)
&& a.GroupCode.Equals(b.GroupCode)
.