簡體   English   中英

如何使用 IEqualityComparer

[英]How to use the IEqualityComparer

我的數據庫中有一些相同編號的鈴鐺。 我想在不重復的情況下獲得所有這些。 我創建了一個比較 class 來完成這項工作,但是 function 的執行導致 function 的很大延遲。從 0.6 秒到 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;
    }
}

您的GetHashCode實現始終返回相同的值。 Distinct依賴於一個好的 hash function 來高效工作,因為它在內部構建了一個hash 表

在實現類的接口時,閱讀文檔很重要,以了解您應該實現哪個合約。 1

在您的代碼中,解決方案是將GetHashCode轉發到Class_reglement.Numf.GetHashCode並在那里適當地實現它。

除此之外,您的Equals方法充滿了不必要的代碼。 它可以重寫如下(相同的語義,1/4 的代碼,更具可讀性):

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

最后, ToList調用是不必要且耗時的: AddRange接受任何IEnumerable ,因此不需要轉換為List AsEnumerable在這里也是多余的,因為在AddRange中處理結果無論如何都會導致這種情況。


1編寫代碼而不知道它實際做什么被稱為貨物崇拜編程 這是一種令人驚訝的普遍做法。 它根本不起作用。

試試這段代碼:

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(); 

如果您想要一個基於 class 的屬性(充當鍵)為您的 class 創建 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);

只需編碼,實現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());

包含您的比較 class (或更具體地說,您需要使用它來使其工作的AsEnumerable調用)意味着排序邏輯從基於數據庫服務器變為位於數據庫客戶端(您的應用程序)上。 這意味着您的客戶端現在需要檢索並處理大量記錄,這總是比在可以使用適當索引的數據庫上執行查找效率低。

您應該嘗試開發一個滿足您的要求的 where 子句,有關更多詳細信息,請參閱使用帶有 LINQ 的 IEqualityComparer 到實體除外子句

IEquatable<T>可以是使用現代框架執行此操作的一種更簡單的方法。

你會得到一個簡單的bool Equals(T other) function 並且不會亂用鑄造或創建單獨的 class。

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 
    }
}

請注意,如果在字典中使用它或使用類似Distinct的東西,則必須實現GetHashCode

PS。 我不認為任何自定義 Equals 方法直接在數據庫端與實體框架一起使用(我認為您知道這一點是因為您使用 AsEnumerable),但這是一種更簡單的方法,可以在一般情況下執行簡單的 Equals。

如果事情似乎不起作用(例如在執行 ToDictionary 時出現重復鍵錯誤),請在 Equals 中放置一個斷點以確保它被命中並確保您已定義GetHashCode (使用 override 關鍵字)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM