111

データベースに同じ番号のベルがいくつかあります。重複せずに全部手に入れたいです。この作業を行うために比較クラスを作成しましたが、関数を実行すると、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;
    }
}
4

7 に答える 7

184

実装GetHashCodeは常に同じ値を返します。内部でハッシュテーブルDistinctを作成するため、効率的に機能するために優れたハッシュ関数に依存しています。

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

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

それとは別に、あなたのEqualsメソッドは不要なコードでいっぱいです。次のように書き直すことができます(同じセマンティクス、コードの1/4、より読みやすい):

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

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


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

于 2011-07-14T14:11:13.623 に答える
52

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

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(); 
于 2014-05-13T06:15:30.933 に答える
6

そのクラスのプロパティ(キーとして機能する)に基づいてクラスのIEqualityComparerを作成する汎用ソリューションが必要な場合は、次を参照してください。

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

    public KeyBasedEqualityComparer(Func<T, TKey> keyGetter)
    {
        if (default(T) == null)
        {
            _keyGetter = (x) => x == null ? default : keyGetter(x);
        }
        else
        {
            _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);
    }
}

構造体のパフォーマンスを向上させるために、ボクシングはありません。

使用法は次のようになります。

IEqualityComparer<Class_reglement> equalityComparer =
  KeyBasedEqualityComparer<Class_reglement>.Create(x => x.Numf);
于 2018-09-30T09:59:28.347 に答える
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());
于 2017-10-23T05:21:04.453 に答える
3

この回答の目的は、以前の回答を次のように改善することです。

  • コンストラクターでラムダ式をオプションにして、プロパティの1つだけでなく、デフォルトで完全なオブジェクトの同等性をチェックできるようにします。
  • サブオブジェクトやネストされたリストなどの複雑なタイプも含め、さまざまなタイプのクラスを操作します。また、プリミティブ型のプロパティのみで構成される単純なクラスだけではありません。
  • 考えられるリストコンテナの違いを考慮していません。
  • ここでは、単純な型(primitifプロパティのみで構成されるもの)でのみ機能する最初の単純なコードサンプルと、完全な(より広範囲のクラスと複雑な型の)2番目の単純なコードサンプルを示します。

これが私の2ペニーの試みです:

public class GenericEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    private Func<T, object> _expr { get; set; }

    public GenericEqualityComparer() => _expr = null;

    public GenericEqualityComparer(Func<T, object> expr) => _expr = expr;

    public bool Equals(T x, T y)
    {
        var first = _expr?.Invoke(x) ?? x;
        var sec = _expr?.Invoke(y) ?? y;

        if (first == null && sec == null)
            return true;

        if (first != null && first.Equals(sec))
            return true;

        var typeProperties = typeof(T).GetProperties();

        foreach (var prop in typeProperties)
        {
            var firstPropVal = prop.GetValue(first, null);
            var secPropVal = prop.GetValue(sec, null);

            if (firstPropVal != null && !firstPropVal.Equals(secPropVal))
                return false;
        }

        return true;
    }

    public int GetHashCode(T obj) =>
        _expr?.Invoke(obj).GetHashCode() ?? obj.GetHashCode();
}

私たちはまだそれを最適化できることを知っています(そしておそらく再帰を使用しますか?)。しかし、それはそれほど複雑でなく、幅広いクラスで魅力のように機能しています。;)

編集: 1日後、これが私の10ドルの試みです:まず、別の静的拡張クラスで、次のものが必要になります:

public static class CollectionExtensions
{
    public static bool HasSameLengthThan<T>(this IEnumerable<T> list, IEnumerable<T> expected)
    {
        if (list.IsNullOrEmptyCollection() && expected.IsNullOrEmptyCollection())
            return true;

        if ((list.IsNullOrEmptyCollection() && !expected.IsNullOrEmptyCollection()) || (!list.IsNullOrEmptyCollection() && expected.IsNullOrEmptyCollection()))
            return false;

        return list.Count() == expected.Count();
    }

    /// <summary>
    /// Used to find out if a collection is empty or if it contains no elements.
    /// </summary>
    /// <typeparam name="T">Type of the collection's items.</typeparam>
    /// <param name="list">Collection of items to test.</param>
    /// <returns><c>true</c> if the collection is <c>null</c> or empty (without items), <c>false</c> otherwise.</returns>
    public static bool IsNullOrEmptyCollection<T>(this IEnumerable<T> list) => list == null || !list.Any();
}

次に、より広範囲のクラスで機能する更新されたクラスを次に示します。

public class GenericComparer<T> : IEqualityComparer<T> where T : class
{
    private Func<T, object> _expr { get; set; }

    public GenericComparer() => _expr = null;

    public GenericComparer(Func<T, object> expr) => _expr = expr;

    public bool Equals(T x, T y)
    {
        var first = _expr?.Invoke(x) ?? x;
        var sec = _expr?.Invoke(y) ?? y;

        if (ObjEquals(first, sec))
            return true;

        var typeProperties = typeof(T).GetProperties();

        foreach (var prop in typeProperties)
        {
            var firstPropVal = prop.GetValue(first, null);
            var secPropVal = prop.GetValue(sec, null);

            if (!ObjEquals(firstPropVal, secPropVal))
            {
                var propType = prop.PropertyType;

                if (IsEnumerableType(propType) && firstPropVal is IEnumerable && !ArrayEquals(firstPropVal, secPropVal))
                    return false;

                if (propType.IsClass)
                {
                    if (!DeepEqualsFromObj(firstPropVal, secPropVal, propType))
                        return false;

                    if (!DeepObjEquals(firstPropVal, secPropVal))
                        return false;
                }
            }
        }

        return true;
    }

    public int GetHashCode(T obj) =>
        _expr?.Invoke(obj).GetHashCode() ?? obj.GetHashCode();

    #region Private Helpers

    private bool DeepObjEquals(object x, object y) =>
        new GenericComparer<object>().Equals(x, y);

    private bool DeepEquals<U>(U x, U y) where U : class =>
        new GenericComparer<U>().Equals(x, y);

    private bool DeepEqualsFromObj(object x, object y, Type type)
    {
        dynamic a = Convert.ChangeType(x, type);
        dynamic b = Convert.ChangeType(y, type);
        return DeepEquals(a, b);
    }

    private bool IsEnumerableType(Type type) =>
        type.GetInterface(nameof(IEnumerable)) != null;

    private bool ObjEquals(object x, object y)
    {
        if (x == null && y == null) return true;
        return x != null && x.Equals(y);
    }

    private bool ArrayEquals(object x, object y)
    {
        var firstList = new List<object>((IEnumerable<object>)x);
        var secList = new List<object>((IEnumerable<object>)y);

        if (!firstList.HasSameLengthThan(secList))
            return false;

        var elementType = firstList?.FirstOrDefault()?.GetType();
        int cpt = 0;
        foreach (var e in firstList)
        {
            if (!DeepEqualsFromObj(e, secList[cpt++], elementType))
                return false;
        }

        return true;
    }

    #endregion Private Helpers

それでも最適化することはできますが、試してみる価値はあります^^。

于 2022-02-09T10:03:14.637 に答える
2

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

代わりに、要件を満たすwhere句を開発する必要があります。詳細については、「 LINQtoEntitiesExcept句でのIEqualityComparerの使用」を参照してください。

于 2011-07-14T14:12:14.743 に答える
1

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キーワードを使用して)定義したことを確認します。

于 2018-01-16T21:42:23.013 に答える