私は現実世界のベスト プラクティス、他の人が複雑なドメインでソリューションを実装する方法を探しています。
6 に答える
の使用を検討するときはいつでも、一時停止して、代わりにIEqualityComparer<T>クラスを実装できるかどうかを考えてください。IEquatable<T>aProductを常に ID で比較する必要がある場合は、デフォルトの比較子を使用できるように、そのように同等に定義するだけです。
とはいえ、カスタム比較機能が必要になる理由はいくつかあります。
- クラスのインスタンスが等しいと見なされる方法が複数ある場合。この最も良い例は、フレームワークが で 6 つの異なる比較子を提供する文字列
StringComparerです。 - として定義できないような方法でクラスが定義されている場合
IEquatable<T>。これには、他の人によって定義されたクラスと、コンパイラによって生成されたクラス (特に、既定でプロパティごとの比較を使用する匿名型) が含まれます。
比較子が必要だと判断した場合は、一般化された比較子を使用できますが (DMenT の回答を参照)、そのロジックを再利用する必要がある場合は、専用のクラスにカプセル化する必要があります。ジェネリック ベースから継承して宣言することもできます。
class ProductByIdComparer : GenericEqualityComparer<ShopByProduct>
{
public ProductByIdComparer()
: base((x, y) => x.ProductId == y.ProductId, z => z.ProductId)
{ }
}
使用する限り、可能な場合は比較子を利用する必要があります。たとえばToLower()、ディクショナリ キーとして使用されるすべての文字列を呼び出すのではなく (アプリケーション全体に散らばるロジック)、大文字と小文字を区別しない を使用するようにディクショナリを宣言する必要がありますStringComparer。比較子を受け入れる LINQ 演算子についても同じことが言えます。しかし、繰り返しになりますが、外部で定義するのではなく、クラスに固有の動作と同等である必要があるかどうかを常に検討してください。
私は次のことを行いました。それが実際のベストプラクティスであるかどうかはわかりませんが、私にとっては問題なく機能しました。:)
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, T, Boolean> _comparer;
private Func<T, int> _hashCodeEvaluator;
public GenericEqualityComparer(Func<T, T, Boolean> comparer)
{
_comparer = comparer;
}
public GenericEqualityComparer(Func<T, T, Boolean> comparer, Func<T, int> hashCodeEvaluator)
{
_comparer = comparer;
_hashCodeEvaluator = hashCodeEvaluator;
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return _comparer(x, y);
}
public int GetHashCode(T obj)
{
if(obj == null) {
throw new ArgumentNullException("obj");
}
if(_hashCodeEvaluator == null) {
return 0;
}
return _hashCodeEvaluator(obj);
}
#endregion
}
その後、コレクションで使用できます。
var comparer = new GenericEqualityComparer<ShopByProduct>((x, y) => x.ProductId == y.ProductId);
var current = SelectAll().Where(p => p.ShopByGroup == group).ToList();
var toDelete = current.Except(products, comparer);
var toAdd = products.Except(current, comparer);
カスタムGetHashCode()機能をサポートする必要がある場合は、代替コンストラクターを使用して、代替計算を行うためのラムダを提供します。
var comparer = new GenericEqualityComparer<ShopByProduct>(
(x, y) => { return x.ProductId == y.ProductId; },
(x) => { return x.Product.GetHashCode()}
);
これがお役に立てば幸いです。=)
(より良い) 代替案については、この投稿を参照してください: IEqualityComparer でデリゲートをラップする
KeyEqualityComparerの部分、特にGetHashCode の重要性に関する部分までスクロールします。(DMenT の投稿で示唆されているように) なぜ間違っているのかについては、全体的な議論があり、代わりに 0 を返す必要があります。obj.GetHashCode();
これは、MSDNが IEqualityComparer (非ジェネリック) について述べていることです。
このインターフェイスを使用すると、コレクションのカスタマイズされた等値比較を実装できます。つまり、等価性の独自の定義を作成し、この定義が
IEqualityComparerインターフェイスを受け入れるコレクション型で使用されるように指定できます。.NET Framework では、、、およびコレクション型のコンストラクターがHashtableこのNameValueCollectionインターフェイスOrderedDictionaryを受け入れます。このインターフェイスは、等価比較のみをサポートします。並べ替えと順序付けの比較のカスタマイズは、
IComparerインターフェイスによって提供されます。
このインターフェースの汎用バージョンは同じ機能を実行するように見えますが、Dictionary<(Of <(TKey, TValue>)>)コレクションに使用されます。
このインターフェイスを独自の目的で使用するためのベスト プラクティスに関する限り。上記の .NET フレームワーク コレクションと同様の機能を持つクラスを派生または実装する場合、および独自のコレクションに同じ機能を追加する場合に、これを使用するのがベスト プラクティスであると言えます。これにより、.NET フレームワークがインターフェイスを使用する方法と一貫性が保たれます。
つまり、カスタム コレクションを開発していて、多くの LINQ およびコレクション関連のメソッド (Sort など) で使用される等価性をコンシューマーが制御できるようにする場合は、このインターフェイスの使用をサポートします。
特定のアルゴリズムにさまざまな等式ルールを組み込む必要がある場合に最適な使用法だと思います。ソート アルゴリズムが を受け入れるのと同じようにIComparer<T>、検索アルゴリズムはIEqualityComparer<T>
リストはこのインターフェースを頻繁に使用するので、a.Substract(b) などの便利な関数を使用できます。
覚えておいてください: オブジェクトが同じハッシュコードを返さない場合、Equals は呼び出されません。