Tupel vs. string als Dictionary-Schlüssel in C#
Ich habe einen cache, den ich implementieren, die mit einem Z. B. concurrentdictionary,
Die Daten, die ich halten muss, hängt davon ab 5 Parameter.
So ist die Methode, um bekommen es aus dem cache ist: (ich zeige nur 3 Parameter hier für die Einfachheit, und ich änderte den Datentyp darstellen CarData für Klarheit)
public CarData GetCarData(string carModel, string engineType, int year);
Ich Frage mich, welche Art von Schlüssel wird nicht besser mit meiner Z. B. concurrentdictionary, kann ich es so machen:
var carCache = new ConcurrentDictionary<string, CarData>();
//check for car key
bool exists = carCache.ContainsKey(string.Format("{0}_{1}_{2}", carModel, engineType, year);
Oder so:
var carCache = new ConcurrentDictionary<Tuple<string, string, int>, CarData>();
//check for car key
bool exists = carCache.ContainsKey(new Tuple(carModel, engineType, year));
Ich glaube nicht, verwenden Sie diese Parameter, zusammen an jedem anderen Ort, so dass es keine Rechtfertigung gibt, eine Klasse zu erstellen, nur um Sie zusammen zu halten.
Möchte ich wissen, welcher Ansatz ist besser in punkto Leistung und Wartungsfreundlichkeit.
- Wenn du redest, Wartbarkeit und Lesbarkeit, ich würde sagen, Sie würde immer noch eine Klasse für die Parameter/Schlüssel mit einem benutzerdefinierten comparer. Wenn Sie eine Klasse verwenden, müssen Sie nur Bearbeiten, sagte der Klasse, und Sie müssen nicht um die Bearbeitung von zwei oder mehr stellen in Ihrem code. Das ist alles meine Meinung, obwohl. Wenn ich hätte wählen müssen zwischen zwei Optionen, würde ich gehen für das Tupel als Schlüssel. Es ist schlimmer, performance-wise, aber es ist einfacher zu verstehen/zu pflegen.
- Ich bevorzuge Leistung mehr Verständnis, wenn der cahce wird halten Hunderte von Schlüsseln und die bekommen Sie, langsam, niemand kümmert sich um code, der besser aussieht...
- Meine Firma immer die Folgendes besagt: "Sie schreiben Ihren code einmal, aber Ihr code wird gelesen, tausend mal." Es ist alles bis zu Ihnen, obwohl, ich finde einfach, dass die Lesbarkeit ist wichtig für den code, den ich Schreibe 🙂
- Nur als RandomStranger, ich würde es vorziehen, eine Klasse für Wartbarkeit, sondern auch für die Leistung. Innerhalb Ihrer eigenen Klasse, könnte man überschreiben
GetHashCode
basiert auf der einzigartigen Wert und Kurzschluss derEquals
basiert auf dem Vergleich der einzigartigsten Werte ersten.GetHashCode
sollte so einfach wie möglich, während noch der Rückkehr einen Wert, der so einzigartig ist wie werden kann für eine optimale Wörterbuch-Leistung - Ich finde es seltsam, dass Sie benötigen würde, 5 verschiedene Schlüssel für das gleiche Wörterbuch.
- Wenn Sie die Verwendung von string-Verkettung, dann werden Sie brauchen, um sehr sicher, dass Sie nicht doppelte Schlüssel. Wenn einer Ihrer strings enthalten _, dann können Sie sofort zweideutig. Es kann natürlich sein, dass dies nicht geschehen kann mit dem format der Schlüssel, aber es ist etwas bewusst zu sein und deshalb würde ich eher geneigt sein, in Richtung Tupel als strings.
- Ich habe in ähnlichen Situationen, wo der code ist im Grunde caching-eine teure Methode aufrufen, die in einem Wörterbuch. Die Wörterbuch-Taste muss auch gemacht werden, bis alle Parameter für die Methode.
- Mein Moto ist: "Immer code, als ob der Kerl, der landet pflegen Sie Ihren code ein gewalttätiger psychopath, der weiß, wo Sie Leben." 🙂
- ein sehr gutes Beispiel.
- Danke für die Idee für das UnitTest, die strings enthalten nicht '_', das ist, warum ich wählte es in der string-Taste
- Die Saiten dürfen nicht enthalten _ jetzt, aber können Sie sagen, für bestimmte, dass Sie nie in der Zukunft? Mit einem Tupel oder eine benutzerdefinierte Klasse, die verhindert, dass diese brechen in der Zukunft, wenn die möglichen Inhalte des keys ändert.
- "Ich glaube nicht, verwenden Sie diese Parameter, zusammen an jedem anderen Ort, so dass es keine Rechtfertigung gibt, eine Klasse zu erstellen" - Was für ein schreckliches Missverständnis. Ich hoffe, dass ist nicht üblich.
- Die Parameter, die an diese Methode sind Teil der verschiedenen Klassen, die es in den code. Diese Methode ist der einzige Ort, dass ich Sie zusammen. Ich denke, dass werde ich gerne die Klassen dieser code anders zu beheben dieser Störung. Können Sie mir sagen, Ihre Vorstellung, wenn Sie denken, ich bin so falsch... Danke für die Ehrlichkeit
- Ich nahm jede Ratschläge und verwendet Tim die Implementierung über eine Klasse. Der code ist jetzt übersichtlicher und ich bin zufrieden mit dem Ergebnis
Du musst angemeldet sein, um einen Kommentar abzugeben.
Du könntest eine Klasse erstellen (egal, dass Ihr nur hier), dass GetHashCode und Equals überschreibt:
Dank theDmi (und andere) für Verbesserungen...
Wenn Sie nicht überschreiben die, ContainsKey funktioniert eine Referenz entspricht.
Hinweis: die
Tuple
Klasse hat seine eigenen Gleichheit von Funktionen, die wäre im Grunde das gleiche tun wie oben. Mit einer maßgeschneiderten Klasse macht deutlich, dass ist, was soll passieren - und ist daher besser für die Wartbarkeit. Es hat auch den Vorteil, daß Sie die Namen der Eigenschaften, so ist es klar,Hinweis 2: die Klasse ist unveränderlich als dictionary-Schlüssel müssen sein, vermeiden Sie mögliche Fehler mit hashcodes ändern, nachdem das Objekt wird dem Wörterbuch Hinzugefügt Siehe hier
GetHashCode hier
Key
implementiertIEquatable<Key>
die autodocuments besser der Typ den Wert Semantik der equalitiy überprüfen.Tuple<T1, T2, T3>
bereits implementiert ist-Vergleich-Logik?GetHashCode
. Wie es scheint, explizit reimplementing einen stub Tupel, nur für verbose. Eine unnötige Elemente in der Architektur ist nicht eine gute Sache. Dieser Ansatz ist gut, aber nur, wenn die vorhandenen standard-Typen wäre nicht ausreichend.GetHashCode
trivial kollidiert parameter setzt, woparam1 == param2
. Dies kann oder kann nicht ein Problem in der Praxis, aber es ist erwähnenswert. (Methoden, um dies zu vermeiden, zählen multiply-add-hashes mit kleinen Konstanten.)Tuple
zumindest verhindert dies nativ.Tuple
ist eine gute Idee, zumindest aus der Produktivität Sicht. Immer alle diese details die richtige für Ihren eigenen separaten Klasse ist eine lästige Pflicht. 🙂Wie immer, Sie haben die Werkzeuge, um es herauszufinden. - Code beide möglichen Lösungen und machen Ihnen Rennen. Der, der gewinnt, ist der Gewinner, du brauchst niemanden hier zu beantworten diese Frage.
Über die Wartung, die Lösung, die autodocuments sich besser und hat eine bessere Skalierbarkeit sollte der Sieger sein. In diesem Fall, der code ist so trivial, dass autodocumentation nicht, dass viel von einem Problem. Von einer Skalierbarkeit Sicht, IMHO, die beste Lösung ist die Verwendung
Tuple<T1, T2, ...>
:pflegen.
Kollisionen sind nicht möglich, etwas, das ist nicht wahr, wenn Sie wählen
die string-Verkettung Lösung:
Ja, weit hergeholt, aber in der Theorie, durchaus möglich, und deine Frage ist genau über unbekannte Ereignisse in der Zukunft, so...
Und last, but not least, der compiler hilft Sie erhalten den code. Wenn, zum Beispiel, morgen müssen Sie hinzufügen
param4
zu deinem SchlüsselTuple<T1, T2, T3, T4>
stark geben Sie Ihre Schlüssel. Ihre string-Verkettung-Algorithmus auf der anderen Seite kann live auf glückseliger generieren von Schlüsseln ohneparam4
und Sie nicht wissen, was passiert, bis Ihr Kunde ruft Sie an, weil Ihre software nicht wie erwartet funktioniert.Wenn Leistung wirklich wichtig ist, dann die Antwort ist, dass Sie sollten nicht verwenden Sie entweder option, da beide unnötig Zuweisung eines Objekts an jedem Zugang.
Verwenden Sie stattdessen eine
struct
entweder eine benutzerdefinierte oderValueTuple
aus das System.ValueTuple Paket:C# 7.0 auch contais syntax-Zucker, damit dieser code einfacher zu schreiben (aber Sie brauchen nicht zu warten, für C# 7.0 zu starten, verwenden
ValueTuple
ohne Zucker):Implementieren Sie eine benutzerdefinierte key-Klasse und stellen Sie sicher, es ist geeignet für solche Fälle, d.h. implementieren
IEquatable
und machen die Klasse unveränderlich:Dies ist ein
GetHashCode()
Umsetzung wie Resharper erzeugt. Es ist eine gute Allgemeine Zweck-Umsetzung. Anpassung als erforderlich.Alternativ, verwenden Sie so etwas wie Equ (ich bin der Ersteller der Bibliothek), die automatisch generiert
Equals
undGetHashCode
Implementierungen. Dies wird sicherstellen, dass diese Methoden immer sind alle Mitglieder desCacheKey
Klasse, so der code wird viel einfacher zu pflegen. Eine solche Umsetzung würde dann einfach so Aussehen:Hinweis: sollten Sie natürlich verwenden sinnvolle Namen der Eigenschaft, da sonst die Einführung einer custom class nicht viel Vorteil gegenüber der Verwendung einer
Tuple
.Tuple
ist, dassItem1
undItem2
nicht bedeuten, dass irgendetwas, das gleiche gilt fürParam1
, undParam2
. Ich würde vorschlagen, einschließlich einer beachten, dass, wenn Sie gehen, um die Mühe der Erstellung einer dedizierten Klasse für diesen Fall, dass Sie gehen sollten, den vollen hog und klar-name jeder parameter, weil alles, was ich sehe sind hier 2 strings und ein int. Vielleicht können Sie herausfinden, was das in ist, aber wie sind Sie zu wissen, welche Zeichenfolge geht wo? (P. S. +1 für unveränderlich und tragfähigeGetHashCode
Umsetzung)IStructuralEquatable
das ist, was ich glaubeTuple<>
ist? Zumindest glaube ich, dass dieser Anwendungsfall ist mindestens mit der teilweise (vielleicht ganz), warum diese Schnittstelle existiertIch wollte zum vergleichen der
Tuple
versusClass
versus "id_id_id" Ansätze beschrieben, die in den anderen Kommentaren. Ich habe diesen einfachen code:Ergebnisse:
Lief ich jede Methode 3 mal in der Version ohne debugging, jedem Lauf auskommentieren die Aufrufe der anderen Methoden. Ich nahm den Durchschnitt der 3 läuft, aber es war nicht viel Varianz sowieso.
TestTuple:
TestClass:
TestFlat:
War ich überrascht zu sehen, dass die class-Ansatz war schneller als beide Tupel-Ansatz und die string-Ansatz. Meiner Meinung nach ist es besser lesbar und mehr Zukunft-sicher, in dem Sinne, dass mehr Funktionalität kann Hinzugefügt werden, um die
Key
Klasse (vorausgesetzt, es ist nicht nur ein Schlüssel, der etwas darstellt).IMHO, bevorzuge ich in solchen Fällen ein intermediate-Struktur zu (in Ihrem Fall es wird sein
Tuple
). Ein solcher Ansatz schafft eine zusätzliche Schicht zwischen parameters und end-Ziel-Wörterbuch. Natürlich wird es davon abhängen Zwecke. So können Sie zum Beispiel schaffen, nicht triviale übergang von Parametern (zum Beispiel container kann die "verzerren" - Daten).Lief ich Tomer test-Fällen, hinzufügen ValueTuples als Testfall (neue c# - value type). War beeindruckt, wie gut Sie durchgeführt.
Code für den test unter: