Frage

Ich habe einen unveränderlichen Wert Objekt, IPathwayModule, deren Wert ist wie folgt definiert:

  • (int) blockieren;
  • (Entity) Modul, identifiziert durch (string) ModuleId;
  • (ENUM) Status; und
  • (Einheit) Klasse, die von (string) ClassId -., Die null sein kann

Hier ist meine aktuelle IEqualityComparer Implementierung, die in wenigen Unit-Tests zur Arbeit erscheint. Aber ich glaube nicht, ich verstehe, was ich gut genug tun, zu wissen, ob ich es richtig mache. Eine frühere Implementierung würde manchmal auf wiederholte Testläufe nicht.

private class StandardPathwayModuleComparer : IEqualityComparer<IPathwayModule>
{
    public bool Equals(IPathwayModule x, IPathwayModule y)
    {
        int hx = GetHashCode(x);
        int hy = GetHashCode(y);
        return hx == hy;
    }

    public int GetHashCode(IPathwayModule obj)
    {
        int h;
        if (obj.Class != null)
        {
            h = obj.Block.GetHashCode() + obj.Module.ModuleId.GetHashCode() + obj.Status.GetHashCode() + obj.Class.ClassId.GetHashCode();
        }
        else
        {
            h = obj.Block.GetHashCode() + obj.Module.ModuleId.GetHashCode() + obj.Status.GetHashCode() + "NOCLASS".GetHashCode();
        }
        return h;
    }
}

IPathwayModule ist definitiv unveränderlich und verschiedene Instanzen mit den gleichen Werten sollen die gleiche HashCode gleich und produzieren, da sie als Elemente innerhalb HashSets verwendet werden.

Ich nehme an, meine Fragen sind:

  • Bin ich die Schnittstelle richtig in diesem Fall mit?
  • Gibt es Fälle, in denen ich vielleicht nicht das gewünschte Verhalten sehen?
  • Gibt es eine Möglichkeit die Robustheit zu verbessern, Leistung?
  • Gibt es gute Praktiken, die ich verfolge nicht?
War es hilfreich?

Lösung 7

Danke an alle, die geantwortet haben. Ich habe das Feedback von jedem aggregiert, die geantwortet und meine verbesserten IEqualityComparer sehen nun wie:

private class StandardPathwayModuleComparer : IEqualityComparer<IPathwayModule>
{
    public bool Equals(IPathwayModule x, IPathwayModule y)
    {
        if (x == y) return true;
        if (x == null || y == null) return false;

        if ((x.Class == null) ^ (y.Class == null)) return false;

        if (x.Class == null) //and implicitly y.Class == null
        {
            return x.Block.Equals(y.Block) && x.Status.Equals(y.Status) && x.Module.ModuleId.Equals(y.Module.ModuleId);
        }
        return x.Block.Equals(y.Block) && x.Status.Equals(y.Status) && x.Module.ModuleId.Equals(y.Module.ModuleId) && x.Class.ClassId.Equals(y.Class.ClassId);
    }
    public int GetHashCode(IPathwayModule obj)
    {
        unchecked {
            int h = obj.Block ^ obj.Module.ModuleId.GetHashCode() ^ (int) obj.Status;
            if (obj.Class != null)
            {
               h ^= obj.Class.ClassId.GetHashCode();
            }
            return h;
        }
    }
}

Andere Tipps

Tun Sie das nicht die Equals in Bezug auf die Ergebnisse der Hash-Funktion es zu zerbrechlich ist. Vielmehr tun ein Feld Wertvergleich für die einzelnen Felder. So etwas wie:

return x != null && y != null && x.Name.Equals(y.Name) && x.Type.Equals(y.Type) ...

Auch die Hash-Funktionen Ergebnisse zusätzlich nicht wirklich zugänglich. Versuchen Sie es mit den ^ Operator statt.

return obj.Name.GetHashCode() ^ obj.Type.GetHashCode() ...

Sie brauchen nicht die Null-Check-in GetHashCode. Wenn dieser Wert null ist, haben Sie größere Probleme bekommen, kein Sinn, von etwas zu erholen, über die Sie keine Kontrolle haben ...

Das einzige große Problem ist die Implementierung von Equals. Hash-Codes sind nicht einzigartig, können Sie den gleichen Hash-Code für Objekte erhalten, die unterschiedlich sind. Sie sollten jedes Feld von IPathwayModule einzeln vergleichen.

GetHashCode () kann ein wenig verbessert werden. Sie brauchen nicht GetHashCode () auf einen int zu nennen. Die int selbst ist ein guter Hash-Code. Das gleiche gilt für ENUM-Werte. Ihre GetHashCode könnte dann wie folgt umgesetzt werden:

public int GetHashCode(IPathwayModule obj)
{
    unchecked {
        int h = obj.Block + obj.Module.ModeleId.GetHashCode() + (int) obj.Status;
        if (obj.class != null)
           h += obj.Class.ClassId.GetHashCode();
        return h;
    }
}

Der ‚ungeprüft‘ Block ist notwendig, weil es in den arithmetischen Operationen seinen Überlauf kann.

Sie sollten nicht GetHashCode () als Haupt Art und Weise der Vergleichsobjekte verwenden. Vergleichen Sie es Feld-weise.

Es können mehrere Objekte mit dem gleichen Hash-Code sein (dies wird als 'Hash-Code Kollisionen').

Auch vorsichtig sein, wenn sie zusammen Werte mehr Integer hinzufügen, da Sie leicht eine Overflow verursachen können. Verwenden Sie 'exklusiv oder' (^) zu Hashcodes oder Wrap-Code in 'abgehakt' Block zu kombinieren.

Sie sollten besser Versionen von Equals implementieren und GetHashCode.

Zum Beispiel kann der Hash-Code von Aufzählungen ist einfach ihr numerischer Wert.

Mit anderen Worten, mit diesen beiden Aufzählungen:

public enum A { x, y, z }
public enum B { k, l, m }

Dann bei der Implementierung, der folgenden Wert ein:

public struct AB {
    public A;
    public B;
}

die beiden folgenden Werte gelten würde gleich:

AB ab1 = new AB { A = A.x, B = B.m };
AB ab2 = new AB { A = A.z, B = B.k };

Ich nehme an, Sie wollen nicht, dass.

Auch die Werttypen als Schnittstellen geben wird sie boxen, diese Bedenken haben Leistung konnte, wenn auch wahrscheinlich nicht viel. Sie betrachten könnten die IEqualityComparer Implementierung direkt Ihre Werttypen nehmen zu machen.

  1. Unter der Annahme, dass zwei Objekte gleich sind, weil ihr Hash-Code gleich ist, ist falsch. Sie müssen alle Mitglieder vergleichen einzeln
  2. Es ist proabably besser ^ eher zu verwenden als + den Hash-Codes zu kombinieren.

Wenn ich dich gut verstehen, dann würden Sie gerne einige Kommentare zu Ihrem Code hören. Here're meine Bemerkungen:

  1. GetHashCode sollte EXOR'ed zusammen, nicht hinzugefügt. XOR (^) gibt eine bessere Chance, Zusammenstöße zu verhindern
  2. Sie vergleichen Hashcodes. Das ist gut, aber dies nur tun, wenn das zugrunde liegende Objekt der GetHashCode außer Kraft setzt. Wenn nicht, Gebrauchseigenschaften und ihre Hashcodes und kombinieren sie.
  3. Hash-Codes sind wichtig, sie machen eine schnelle möglich vergleichen. Aber wenn Hash-Codes gleich sind, kann das Objekt noch unterschiedlich sein. Dies geschieht selten. Aber Sie werden die Felder des Objekts vergleichen müssen, wenn Hash-Codes gleich sind.
  4. Sie sagen, dass Ihre Werttypen sind unveränderlich, aber Sie Referenzobjekte (.Class), die nicht unveränderlich sind
  5. Immer optimize Vergleich anhand Vergleich als erster Test hinzufügen. Referenzen ungleich, die Objekte ungleich sind, dann sind die Strukturen ungleich.

Punkt 5 hängt davon ab, ob die Sie die Objekte wollen, dass Sie in Ihrem Wert Typ verweisen, nicht zurückzukehren gleich, wenn nicht die gleiche Referenz.

EDIT: Sie viele Strings vergleichen. Der String-Vergleich wird in C # optimiert. Sie können, wie andere vorgeschlagen, eine bessere Nutzung == mit ihnen im Vergleich. Für die GetHashCode, Verwendung oder ^ als auch von anderen vorgeschlagen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top