IEqualityComparerの使用方法


97

私のデータベースには同じ数のベルがいくつかあります。重複せずに全部入手したいです。この作業を行うために比較クラスを作成しましたが、関数の実行により、関数から0.6秒から3.2秒に、明確に区別されない大きな遅延が発生します。

私はそれを正しく行っていますか、それとも別の方法を使用する必要がありますか?

reg.AddRange(
    (from a in this.dataContext.reglements
     join b in this.dataContext.Clients on a.Id_client equals b.Id
     where a.date_v <= datefin && a.date_v >= datedeb
     where a.Id_client == b.Id
     orderby a.date_v descending 
     select new Class_reglement
     {
         nom  = b.Nom,
         code = b.code,
         Numf = a.Numf,
     })
    .AsEnumerable()
    .Distinct(new Compare())
    .ToList());

class Compare : IEqualityComparer<Class_reglement>
{
    public bool Equals(Class_reglement x, Class_reglement y)
    {
        if (x.Numf == y.Numf)
        {
            return true;
        }
        else { return false; }
    }
    public int GetHashCode(Class_reglement codeh)
    {
        return 0;
    }
}


このブログは、どのように完璧されたIEqualityComparerを使用するように説明しますblog.alex-turok.com/2013/03/c-linq-and-iequalitycomparer.html
ジェレミー・レイ・ブラウン

回答:


173

あなたのGetHashCode実装では、常に同じ値を返します。Distinctは内部的にハッシュテーブルを構築するため、効率の良いハッシュ関数に依存しています

クラスのインターフェースを実装するときはドキュメント読んで、実装することになっているコントラクトを知ることが重要です。1

あなたのコードでは、解決策はそこに転送GetHashCodeClass_reglement.Numf.GetHashCodeて適切に実装することです。

それとは別に、Equalsメソッドには不要なコードがたくさんあります。次のように書き換えることができます(同じセマンティクス、コードの¼、より読みやすい):

public bool Equals(Class_reglement x, Class_reglement y)
{
    return x.Numf == y.Numf;
}

最後に、ToList呼び出しは不必要で時間がかかります:aへの変換が必要なものAddRangeをすべて受け入れます。で結果を処理すると、とにかくこれが発生するため、ここでも冗長です。IEnumerableListAsEnumerableAddRange


1実際に何をしているのか知らずにコードを書くことをカーゴカルトプログラミングと呼びます。驚くほど広く行われていることです。基本的に機能しません。


19
xまたはyがnullの場合、Equalsは失敗します。
dzendras

4
@dzendrasも同じですGetHashCode。ただし、のドキュメントにIEqualityComparer<T>null引数の処理方法が指定されていないことに注意してください。ただし、記事で提供されている例も処理しませんnull
Konrad Rudolph

49
ワオ。 嫌悪感は不必要に厳しいです。私たちは侮辱ではなく、お互いを助けるためにここにいます。笑わせる人もいると思いますが、削除することをお勧めします。
ジェス

4
ウィキで「カーゴカルトプログラミング」について読んだ後、スカイプのタグラインを「//ディープマジックがここから始まります...その後、重い魔法使いが続きます」という+1。
Alex

4
@NeilBenn率直なアドバイスを無礼と間違えます。OPが答えを受け入れたので(そして、もっとスターナーバージョンでは注意するかもしれませんが)、彼らは同じ間違いを犯したようには見えませんでした。なぜ助言をするのが失礼だと思うのかはわかりませんが、「その人は講義がいらない」と言って間違えています。私は強く反対します。講義必要で、それは心に留められました。書かれているように、コードは悪いものであり、悪い作業慣行に基づいています。これを指摘しないことは悪いことであり、まったく役に立たないでしょう。それ以降、OPはそれらの機能を改善できませんでした。
Konrad Rudolph

47

このコードを試してください:

public class GenericCompare<T> : IEqualityComparer<T> where T : class
{
    private Func<T, object> _expr { get; set; }
    public GenericCompare(Func<T, object> expr)
    {
        this._expr = expr;
    }
    public bool Equals(T x, T y)
    {
        var first = _expr.Invoke(x);
        var sec = _expr.Invoke(y);
        if (first != null && first.Equals(sec))
            return true;
        else
            return false;
    }
    public int GetHashCode(T obj)
    {
        return obj.GetHashCode();
    }
}

その使用例は

collection = collection
    .Except(ExistedDataEles, new GenericCompare<DataEle>(x=>x.Id))
    .ToList(); 

19
GetHashCode式も使用する必要があります。使用方法については、この投稿return _expr.Invoke(obj).GetHashCode();参照してください。
orad 2014

3

実装GetHashCodeNULL検証を伴うコードのみ:

public class Class_reglementComparer : IEqualityComparer<Class_reglement>
{
    public bool Equals(Class_reglement x, Class_reglement y)
    {
        if (x is null || y is null))
            return false;

        return x.Numf == y.Numf;
    }

    public int GetHashCode(Class_reglement product)
    {
        //Check whether the object is null 
        if (product is null) return 0;

        //Get hash code for the Numf field if it is not null. 
        int hashNumf = product.hashNumf == null ? 0 : product.hashNumf.GetHashCode();

        return hashNumf;
    }
}

例:Numfで区別される Class_reglementのリスト

List<Class_reglement> items = items.Distinct(new Class_reglementComparer());

2

比較クラスを含める(具体的には、AsEnumerableそれを機能させるために使用する必要がある呼び出し)とは、並べ替えロジックがデータベースサーバーに基づくものからデータベースクライアント(アプリケーション)に至るまでになったことを意味します。これは、クライアントがより多くのレコードを取得して処理する必要があることを意味します。これは、適切なインデックスを使用できるデータベースでルックアップを実行するよりも常に非効率です。

代わりに、要件を満たすwhere句を開発してみてください。詳細については、LINQ to Entities Except句IEqualityComparerを使用するを参照してください。


2

ボクシングなしの一般的なソリューションが必要な場合:

public class KeyBasedEqualityComparer<T, TKey> : IEqualityComparer<T>
{
    private readonly Func<T, TKey> _keyGetter;

    public KeyBasedEqualityComparer(Func<T, TKey> keyGetter)
    {
        _keyGetter = keyGetter;
    }

    public bool Equals(T x, T y)
    {
        return EqualityComparer<TKey>.Default.Equals(_keyGetter(x), _keyGetter(y));
    }

    public int GetHashCode(T obj)
    {
        TKey key = _keyGetter(obj);

        return key == null ? 0 : key.GetHashCode();
    }
}

public static class KeyBasedEqualityComparer<T>
{
    public static KeyBasedEqualityComparer<T, TKey> Create<TKey>(Func<T, TKey> keyGetter)
    {
        return new KeyBasedEqualityComparer<T, TKey>(keyGetter);
    }
}

使用法:

KeyBasedEqualityComparer<Class_reglement>.Create(x => x.Numf)

0

IEquatable<T> 最新のフレームワークでこれを行うには、はるかに簡単な方法です。

あなたは素晴らしいシンプルなbool Equals(T other)関数を手に入れ、キャストしたり別のクラスを作成したりする必要はありません。

public class Person : IEquatable<Person>
{
    public Person(string name, string hometown)
    {
        this.Name = name;
        this.Hometown = hometown;
    }

    public string Name { get; set; }
    public string Hometown { get; set; }

    // can't get much simpler than this!
    public bool Equals(Person other)
    {
        return this.Name == other.Name && this.Hometown == other.Hometown;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();  // see other links for hashcode guidance 
    }
}

GetHashCodeこれをディクショナリで使用する場合、またはなどで実装する必要があることに注意してくださいDistinct

PS。カスタムEqualsメソッドはデータベース側でエンティティフレームワークと直接連動するとは思いません(AsEnumerableを実行しているので、これはわかっていると思います)が、これは一般的なケースで単純なEqualsを実行するためのはるかに単純なメソッドです。

機能していないように見える場合(ToDictionaryの実行時の重複キーエラーなど)、ブレークポイントをEquals内に置き、ヒットしていることを確認し、GetHashCode(overrideキーワードを使用して)定義したことを確認します。


1
nullを確認する必要があります
disklosr 2018年

私はそれに遭遇したことはありませんが、次回はそうすることを覚えています。List <T>などにnullがありますか?
Simon_Weaver 2018年

1
代わりに、.Equals()あなたother.Hometown自身と比較したように見える方法の下でthis.Hometown
ジェイク・ストークス

おっとっと。誤植を修正しました:)
Simon_Weaver
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.