Verwendung des IEqualityComparer


96

Ich habe einige Glocken in meiner Datenbank mit der gleichen Nummer. Ich möchte sie alle ohne Vervielfältigung erhalten. Ich habe eine Vergleichsklasse erstellt, um diese Arbeit zu erledigen, aber die Ausführung der Funktion verursacht eine große Verzögerung der Funktion ohne Unterschied, von 0,6 Sekunden bis 3,2 Sekunden!

Mache ich es richtig oder muss ich eine andere Methode anwenden?

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
Vielleicht möchten Sie einen Blick auf Richtlinien und Regeln für GetHashCode
Conrad Frix

Dieser Blog erklärt, wie man den IEqualityComparer perfekt benutzt: blog.alex-turok.com/2013/03/c-linq-and-iequalitycomparer.html
Jeremy Ray Brown

Antworten:


173

Ihre GetHashCodeImplementierung gibt immer den gleichen Wert zurück. Distinctstützt sich auf eine gute Hash-Funktion, um effizient zu arbeiten, da intern eine Hash-Tabelle erstellt wird .

Bei der Implementierung von Schnittstellen von Klassen ist es wichtig, die Dokumentation zu lesen , um zu wissen, welchen Vertrag Sie implementieren sollen. 1

In Ihrem Code besteht die Lösung darin, ihn dort weiterzuleiten und entsprechend GetHashCodezu Class_reglement.Numf.GetHashCodeimplementieren.

Abgesehen davon ist Ihre EqualsMethode voller unnötigen Codes. Es könnte wie folgt umgeschrieben werden (gleiche Semantik, ¼ des Codes, besser lesbar):

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

Schließlich ist der ToListAnruf unnötig und zeitaufwändig: AddRangeAkzeptiert alle, IEnumerablesodass eine Konvertierung in a Listnicht erforderlich ist. AsEnumerableist auch hier redundant, da die Verarbeitung des Ergebnisses in AddRangedies ohnehin verursacht.


1 Das Schreiben von Code, ohne zu wissen, was er tatsächlich tut, wird als Frachtkultprogrammierung bezeichnet . Es ist eine überraschend weit verbreitete Praxis. Es funktioniert grundsätzlich nicht.


18
Ihre Gleichheit schlägt fehl, wenn entweder x oder y null ist.
Dzendras

4
@dzendras Gleiches für GetHashCode. Beachten Sie jedoch, dass in der Dokumentation vonIEqualityComparer<T> nicht angegeben ist, was mit nullArgumenten zu tun ist. Die im Artikel enthaltenen Beispiele behandeln dies jedoch auch nicht null.
Konrad Rudolph

48
Beeindruckend. Greuel ist unnötig hart. Wir sind hier, um uns gegenseitig zu helfen, nicht um zu beleidigen. Ich denke, es bringt einige Leute zum Lachen, aber ich würde empfehlen, es zu entfernen.
Jess

4
+1 dafür, dass ich im Wiki über "Cargo Cult Programming" gelesen und dann meine Skype-Tag-Zeile in "// Deep Magic beginnt hier ... gefolgt von schwerer Zauberei" geändert habe.
Alex

4
@NeilBenn Du verwechselst offene Ratschläge mit Unhöflichkeit. Da OP die Antwort akzeptierte (und ich könnte bemerken, in einer viel strengeren Version!), Schienen sie nicht den gleichen Fehler zu machen. Ich bin mir nicht sicher, warum Sie Ratschläge für unhöflich halten, aber Sie irren sich, wenn Sie sagen, dass „der Typ keinen Vortrag braucht“. Ich bin absolut anderer Meinung: Der Vortrag wurde gebraucht und zu Herzen genommen. Der geschriebene Code war schlecht und basierte auf schlechter Arbeitspraxis. Nicht darauf hinzuweisen wäre ein schlechter Dienst und überhaupt nicht hilfreich, da das OP seitdem ihre Funktionsweise nicht verbessern konnte.
Konrad Rudolph

46

Versuchen Sie diesen Code:

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

Beispiel für seine Verwendung wäre

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

19
GetHashCodeBedarf den Ausdruck verwenden , wie gut: return _expr.Invoke(obj).GetHashCode();Sehen Sie diesen Beitrag für eine Verwendung davon.
Orad

3

Nur Code mit Implementierung GetHashCodeund NULLValidierung:

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

Beispiel: Liste von Class_reglement, die sich durch Numf unterscheidet

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

2

Die Einbeziehung Ihrer Vergleichsklasse (oder genauer gesagt des AsEnumerableAufrufs, den Sie verwenden mussten, um sie zum Laufen zu bringen) bedeutete, dass die Sortierlogik nicht mehr auf dem Datenbankserver, sondern auf dem Datenbankclient (Ihrer Anwendung) basierte. Dies bedeutete, dass Ihr Client jetzt eine größere Anzahl von Datensätzen abrufen und dann verarbeiten muss. Dies ist immer weniger effizient als die Suche in der Datenbank, in der die entsprechenden Indizes verwendet werden können.

Sie sollten versuchen, stattdessen eine where-Klausel zu entwickeln, die Ihren Anforderungen entspricht. Weitere Informationen finden Sie unter Verwenden eines IEqualityComparer mit einer LINQ to Entities Except-Klausel .


2

Wenn Sie eine generische Lösung ohne Boxen wollen:

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

Verwendung:

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

0

IEquatable<T> kann mit modernen Frameworks viel einfacher sein.

Sie erhalten eine schöne einfache bool Equals(T other)Funktion und es gibt kein Herumspielen beim Casting oder Erstellen einer separaten Klasse.

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

Beachten Sie, dass Sie dies implementieren müssen, GetHashCodewenn Sie dies in einem Wörterbuch oder mit etwas Ähnlichem verwenden Distinct.

PS. Ich glaube nicht, dass benutzerdefinierte Equals-Methoden mit dem Entity-Framework direkt auf der Datenbankseite funktionieren (ich denke, Sie wissen das, weil Sie AsEnumerable ausführen), aber dies ist eine viel einfachere Methode, um einfache Equals für den allgemeinen Fall zu erstellen.

Wenn die Dinge nicht zu funktionieren scheinen (z. B. doppelte Schlüsselfehler beim Ausführen von ToDictionary), setzen Sie einen Haltepunkt in Equals, um sicherzustellen, dass er getroffen wird, und stellen Sie sicher, dass Sie ihn GetHashCodedefiniert haben (mit Schlüsselwort zum Überschreiben).


1
Sie müssen noch nach null suchen
disklosr

Ich bin nie darauf gestoßen, aber ich werde mich beim nächsten Mal daran erinnern. Haben Sie eine Null in einer Liste <T> oder so ähnlich?
Simon_Weaver

1
Unter der .Equals()Methode scheinen Sie mit other.Hometownsich selbst verglichen zu haben, anstattthis.Hometown
Jake Stokes

Hoppla. Tippfehler behoben :)
behoben
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.