IEqualityComparer nasıl kullanılır?


98

Veritabanımda aynı numaraya sahip bazı çanlar var. Hepsini kopyalamadan almak istiyorum. Bu işi yapmak için bir karşılaştırma sınıfı oluşturdum, ancak işlevin yürütülmesi, işlevden farklı olmaksızın 0.6 sn'den 3.2 sn'ye kadar büyük bir gecikmeye neden oluyor!

Doğru mu yapıyorum yoksa başka bir yöntem mi kullanmalıyım?

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

16
Sen bir göz atmak isteyebilirsiniz GetHashCode Rehberi ve kurallar
Conrad Frix

Bu blog, IEqualityComparer'ın nasıl mükemmel bir şekilde kullanılacağını açıklıyor: blog.alex-turok.com/2013/03/c-linq-and-iequalitycomparer.html
Jeremy Ray Brown

Yanıtlar:


175

Kişisel GetHashCodeuygulanması her zaman aynı değeri döndürür. Distinctverimli bir şekilde çalışmak için iyi bir hash fonksiyonuna güvenir, çünkü dahili olarak bir hash tablosu oluşturur .

Sınıfların arayüzlerini uygularken , hangi sözleşmeyi uygulamanız gerektiğini bilmek için belgeleri okumak önemlidir . 1

Kodunuzda, çözüm iletmek olduğunu GetHashCodeiçin Class_reglement.Numf.GetHashCodeve uygun oraya uygulamak.

Bunun dışında Equalsyönteminiz gereksiz kodlarla doludur. Aşağıdaki gibi yeniden yazılabilir (aynı anlambilim, kodun ¼'si, daha okunaklı):

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

Son olarak, ToListçağrı gereksizdir ve zaman alıcıdır: AddRangeherhangi IEnumerablebir şeyi kabul eder, bu nedenle a'ya dönüştürme Listgerekli değildir. AsEnumerableolduğu da sonucu işleme beri burada yedekli AddRangezaten bu neden olacaktır.


1 Gerçekte ne yaptığını bilmeden kod yazmak, kargo kültü programlama olarak adlandırılır . Şaşırtıcı derecede yaygın bir uygulamadır. Temelde çalışmıyor.


20
Eşitleriniz, x veya y boş olduğunda başarısız olur.
dzendras

4
@dzendras Aynı GetHashCode. Bununla birlikte, belgelerininIEqualityComparer<T>null bağımsız değişkenlerle ne yapılacağını belirtmediğini unutmayın - ancak makalede verilen örnekler nullikisini de ele almaz .
Konrad Rudolph

50
Vay. İğrençlik gereksiz yere serttir. Hakaret değil, birbirimize yardım etmek için buradayız. Sanırım bu bazı insanları güldürüyor, ancak kaldırmanızı tavsiye ederim.
Jess

5
Bana wiki'de "kargo kült programlaması" hakkında bir şeyler okuduktan sonra skype etiket satırımı "// Derin sihir burada başlıyor ... ardından biraz ağır sihirbazlık" olarak değiştirdiğim için +1.
Alex

4
@NeilBenn Açıkça öğütleri kabalıkla karıştırıyorsun. OP cevabı kabul ettiğinden (ve çok daha katı bir versiyonda not edebilirim!), Aynı hatayı yapmamış gibi görünüyorlardı. Neden öğüt vermenin kaba olduğunu düşündüğünden emin değilim ama “adamın derse ihtiyacı yok” dediğinizde yanılıyorsunuz. Ders: Kesinlikle katılmıyorum edildi ihtiyacı vardı ve kalbe götürüldü. Kod yazıldığı şekliyle kötüydü ve kötü çalışma uygulamasına dayanıyordu. Bunu belirtmemek bir kötü hizmet olur ve hiç de yardımcı olmaz, çünkü o zamandan beri OP onların çalışma şeklini geliştiremedi.
Konrad Rudolph

47

Bu kodu deneyin:

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

Kullanım örneği şöyle olacaktır:

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

20
GetHashCodeifadeyi de kullanması gerekiyor: Kullanımı için bu gönderiyereturn _expr.Invoke(obj).GetHashCode(); bakın .
orad

3

Yalnızca uygulama GetHashCodeve NULLdoğrulama ile kod :

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

Örnek: listesi Class_reglement ile ayrı Numf

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

2

Karşılaştırma sınıfınızın dahil edilmesi (veya daha spesifik AsEnumerableolarak çalışmasını sağlamak için kullanmanız gereken çağrı), sıralama mantığının veritabanı sunucusuna dayalı olmaktan veritabanı istemcisine (uygulamanız) dönüştüğü anlamına geliyordu. Bu, müşterinizin artık daha fazla sayıda kaydı alması ve daha sonra işlemesi gerektiği anlamına gelir; bu, uygun dizinlerin kullanılabileceği veritabanında arama yapmaktan her zaman daha az verimli olacaktır.

Bunun yerine gereksinimlerinizi karşılayan bir where cümlesi geliştirmeye çalışmalısınız, daha fazla ayrıntı için LINQ to Entities Except cümlesi ile IEqualityComparer kullanma konusuna bakın.


2

Boks yapmadan genel bir çözüm istiyorsanız:

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

kullanım:

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

0

IEquatable<T> bunu modern çerçevelerle yapmanın çok daha kolay bir yolu olabilir.

Hoş bool Equals(T other)ve basit bir fonksiyon elde edersiniz ve döküm veya ayrı bir sınıf oluşturmakla uğraşmak zorunda kalmazsınız.

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

GetHashCodeBunu bir sözlükte veya benzeri bir şeyle kullanıyorsanız uygulamanız GEREKİR Distinct.

PS. Herhangi bir özel Equals yönteminin doğrudan veritabanı tarafında varlık çerçevesi ile çalıştığını sanmıyorum (bunu AsEnumerable yaptığınız için bildiğinizi düşünüyorum), ancak bu genel durum için basit bir Equals yapmak için çok daha basit bir yöntemdir.

İşler çalışmıyorsa (ToDictionary yaparken yinelenen anahtar hataları gibi), vurulduğundan emin olmak ve GetHashCodetanımladığınızdan emin olmak için (geçersiz kılma anahtar kelimesi ile) Eşittir'in içine bir kesme noktası koyun .


1
Hala
boşluğu

Bununla hiç karşılaşmadım ama bir dahaki sefere bunu yapmayı hatırlayacağım. List <T> veya bunun gibi bir null değeriniz var mı?
Simon_Weaver

1
Altında .Equals()yöntemle size göre var görünüyor other.Hometownyerine, kendisinethis.Hometown
Jake Stokes

Oops. Yazım hatası düzeltildi :)
Simon_Weaver
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.