Wann sollte ich eine Klasse mit 2 Eigenschaften über eine vorgefertigte Struktur wie ein KeyValuePair verwenden?

31

Wann sollten Sie den Datentyp Schlüssel / Wert in eine eigene Klasse einfügen, anstatt eine vordefinierte generische Struktur wie a KeyValuePairoder a zu verwenden Tuple?

Beispielsweise enthalten die meisten von mir erstellten Kombinationsfelder einen Anzeigenamen und einen Wert. Dies ist die Art von Daten, die ich zu entscheiden versuche, wann eine neue Klasse eingefügt werden soll und wann nur ein KeyValuePair verwendet werden soll.

Zur Zeit arbeite ich an etwas , dass Anwendungen iCalendarund die ausgewählten Benutzerdaten werden schließlich zu einem kombinierten key1=value1;key2=value2;Art von String. Ich begann damit, die Daten in ein KeyValuePair<string,string>einzutragen, aber jetzt frage ich mich, ob das stattdessen eine eigene Klasse sein sollte.

Insgesamt bin ich daran interessiert herauszufinden, welche Richtlinien verwendet werden, wenn eine vorhandene Struktur / Klasse wie ein KeyValuePairObjekt mit mehr als 2 Eigenschaften verwendet wird, und in welchen Situationen Sie eine andere verwenden würden.

Rachel
quelle
3
Welche Sprache? In Python haben wir dieses Dilemma nicht, weil wir tupleTyp haben.
S.Lott,
3
@ S.Lott - Die .NET BCL hat Tupel seit v 4.0.
Oded
1
.NET 4 hat auch den Tupeltyp. msdn.microsoft.com/en-us/library/system.tuple.aspx
Dave Nay
1
@Oded In diesem Fall baue ich etwas mit iCalendarund ich wollte Objekte für BYDAYund BYSETPOS. Sie werden in den Kombinationsfeldern angezeigt, aber die tatsächlichen Daten werden in der wiederkehrenden Regelzeichenfolge zusammengefasst, bei der es sich um eine key=value;Zeichenfolgeart handelt
Rachel,
4
@ Rachel: Bitte aktualisieren Sie die Frage, um genauer zu sein. Eine Reihe von Kommentaren ist nicht der beste Weg, um Dinge zu klären.
S.Lott,

Antworten:

26

In den meisten Fällen würde ich eher ein Objekt als ein KeyValuePair oder Tuple verwenden. Erstens, wenn Sie 6 Monate später kommen, um Änderungen vorzunehmen, ist es viel einfacher herauszufinden, was Ihre Absicht früher war, als sich zu fragen, was das Tuple tist und warum es diese lustigen Werte hat. Zweitens können Sie, wenn die Dinge wachsen und sich ändern, Ihren einfachen Datenübertragungsobjekten das gewünschte Verhalten geben. Benötigen Sie zwei Namensformate? Einfach, fügen Sie einfach die entsprechenden ToString () -Überladungen hinzu. Benötigen Sie es, um eine Schnittstelle zu implementieren? Kein Problem. Schließlich ist das Erstellen eines einfachen Objekts wirklich mit nahezu null Aufwand verbunden, insbesondere mit automatischen Eigenschaften und Code-Vervollständigung.

Bonus Protip: Wenn Sie verhindern möchten, dass diese Objekte Ihren Namespace verschmutzen, ist es eine gute Möglichkeit, private Klassen in Klassen zu integrieren, um Probleme zu vermeiden und ungewöhnliche Abhängigkeiten zu vermeiden.

Wyatt Barnett
quelle
1
DTOs = Datenübertragungsobjekte ? - Ich hasse TLAs ;-)
Ben
3
@ Ben - TFTFY. . .
Wyatt Barnett
7

Die Regel zum Definieren einer neuen Klasse ist einfach: Sie wird mit "Occam's Razor" zusammengefasst.

http://c2.com/cgi/wiki?OccamsRazor

Stellen Sie keine neuen Klassen ohne guten Grund vor. Oder verwenden Sie so viele vorgefertigte Klassen wie möglich. Oder erfinde so wenig wie möglich. Sie können jedoch problemlos weniger Code schreiben.

Sie können eine vorgefertigte Klasse nicht verwenden, wenn Sie dem Objekt eine eindeutige Verantwortung zuweisen müssen und diese Verantwortung in der vorgefertigten Klasse nicht definiert ist. Oft ist dies eine einzigartige Methode.

Ein tupleoder KeyValuePairist bevorzugt.

Bis um.

Sie benötigen Funktionen, die nicht Teil von A tupleoder sind KeyValuePair. Dann müssen Sie Ihre eigene Klasse definieren.

S.Lott
quelle
3
" Entwerfe niemals, was du stehlen kannst " ist meine Lieblingsformulierung.
SingleNegationElimination
1
Würden Sie die Semantik als "Funktionalität, die nicht Teil eines tupleoder ist KeyValuePair" betrachten? KeyValuePair hat zumindest eine eingeschränkte Form der Semantik, aber Tupel tun dies selten (möglicherweise in Abhängigkeit vom Kontext) imho. Die Einführung einer neuen Klasse sollte Ihnen selbstverständlich eine klare Semantik geben.
Mal Ross
+1 Flicken Sie nicht ein ganzes Boot mit Klebeband, wenn es einen Stecker passt.
Evan Plaice
4
Ich denke, dies ist eine unangebrachte Verwendung von Occams Rasierer. Zweitens ist es wichtig zu wissen, welche Informationen in einer Variablen enthalten sind. -1.
Bent
4

Wenn Sie Ihre eigene Klasse schreiben, haben Sie einen klaren Platz, um die Dokumentation über die beiden Werte zu platzieren. Zumal zwei Saiten keine sehr klare Definition sind. Allerdings könnte man sowas schreiben

 public class MyPair : Tuple<string, string>
Zeichen
quelle
Ich mag das, weil es MyPairdefiniert, was die Tupel-Werte sind und wofür sie verwendet werden.
IAbstrakter
2
Aber dann würden Ihre Immobilien heißen Item1und Item2! Ist es nicht genauso einfach, die gesamte Klasse zu definieren? public class SideStrings { public string Left { get; set; } public string Right { get; set; } }
Konfigurator
@configurator Ich schrieb eine Antwort darauf und bemerkte, dass Tuple nur lesbar ist, also stimme ich zu, was meiner Meinung nach das Ganze in Frage stellt. Sie können das Tupel auch umbrechen, um die gewünschten Werte zu benennen.
Anmeldung
2
@Sign: Aber worum geht es? Das Erstellen einer eigenen Klasse ist weniger aufwendig als das Umschließen eines Tupels.
Konfigurator
2
Mit @configurator erhalten Sie Gleichheits- und Vergleichselemente, die besser funktionieren als ein benutzerdefiniertes Objekt, aber wenn Einstellungen erforderlich sind, ist das Tupel definitiv der falsche Weg.
Anmeldung
4

S.Lott schrieb

Stellen Sie keine neuen Klassen ohne guten Grund vor. Oder verwenden Sie so viele vorgefertigte Klassen wie möglich. Erfinde so wenig wie möglich.

Sie können keine vorgefertigte Klasse verwenden, wenn Sie dem Objekt, das nicht in der vorgefertigten Klasse definiert ist, eine eindeutige Verantwortung zuweisen müssen.

Lassen Sie mich sagen, warum ich ein ernstes Problem damit habe. Schon seit

KeyValuePair [Zeichenfolge, Zeichenfolge]

scheint in Ordnung zu sein ... ist KeyValue [string, KeyValue [string, KeyValuePair [string, string]]] auch in Ordnung? Ich weiß nicht, was S.Lott dazu sagen wird, aber mein Chef findet es in Ordnung.

Ich traue mich nicht zu widersprechen. Und hier ist der Grund: Es verringert die Lesbarkeit des Codes, der folgen wird. Die Funktion, die eine solche Datenstruktur ausfüllt, ist viel komplexer und fehleranfälliger (err .. exception) (stellen Sie sich zumindest auch eine Geschäftslogik vor). Ich habe nicht zu viel mit meinem Chef gestritten, aber ich sage es hier: Lesbarkeit übertrumpft winzige Platzersparnisse (was sein Argument war). Wäre es nicht ein Klassenobjekt, in dem es einige Felder gibt? aber manche bleiben manchmal leer besser?

PS Ich bin ein Ultra_Noob, also sag mir bitte, wenn ich falsch liege.

Chani
quelle
2
Ich denke, das KeyValuePair<string,string>ist in Ordnung, vorausgesetzt, es handelt sich tatsächlich um Schlüssel und Werte. In diesem Fall ist die Wiederverwendung einer vorhandenen Klasse ein Gewinn, da Sie das Schreiben von Code sparen und die Interoperabilität verbessern. OTOH, etwas so kompliziertes zu verwenden, wie KeyValuePair<string,KeyValuePair<string,string>>es die meiste Zeit einfach zu kompliziert ist, um praktisch zu sein. Manchmal mag es richtig sein - wenn Sie keine zusätzliche Funktionalität benötigen, wenn die Bedeutung offensichtlich ist und wenn es gut mit anderen Klassen zusammenarbeitet. YMMV, das belastet nur die Vor- und Nachteile.
Maaartinus
Wenn Sie C # verwenden, sehen Sie hier die Leistung eines Tupel vs KeyValuePair . Etwas, das Sie vielleicht mit Ihrem Chef teilen möchten?
Ben
3

Wenn wir Schlüssel / Wert- Paar sagen , denke ich normalerweise an Hash-Tabellen oder assoziative Arrays oder einfach an ein KeyValuePair- Objekt. Ich benutze sie alle und es gibt keinen Unterschied, wann ich welche benutze.

Ich denke, das Wichtigste an einer Liste von Schlüssel / Wert-Paaren ist, dass Schlüssel eindeutig sein sollten (da es sich immerhin um einen Schlüssel handelt), und alle oben genannten Strukturen bieten mir wirklich diese Funktionalität.

Ansonsten möchte ich nach Tasten suchen oder Aktionen im Array-Stil wie Push und Pop ausführen.

Meine Antwort lautet also NEIN , und ich erstelle keine Objekte explizit für Schlüssel / Wert-Paare, da viele eingebaute Strukturen dies bereits für mich tun.

Saeed Neamati
quelle
3

Kapitel 6 von Clean Code enthält eine gute Beschreibung, wann eine Datenstruktur im Vergleich zu einer Klasse verwendet werden muss. Kurz gesagt, Sie verwenden eine Datenstruktur, wenn Sie meistens damit rechnen, neue Funktionen hinzuzufügen, um diese Daten zu verarbeiten. Sie verwenden eine Klasse, wenn Sie hauptsächlich damit rechnen, neue Datentypen hinzuzufügen, die von vorhandenen Funktionen verarbeitet werden sollen. Ihre ComboBox ist ein Beispiel für Letzteres. Sie haben eine Reihe von Funktionen (die ComboBox-Implementierung), die mit vielen verschiedenen Datentypen (verschiedenen Datentypen für verschiedene Widgets) arbeiten.

Karl Bielefeldt
quelle
2

Ich würde Wilding hier vielleicht zustimmen , aber dann bin ich auch ein bisschen noob in so etwas.

Ich würde vorschlagen, dass die eingebauten Typen häufig Mechanismusobjekte sind. KeyValuePair zum Beispiel ist Teil des Mechanismus eines Wörterbuchs und von Hash-Tabellen, um eindeutige Schlüssel sicherzustellen, und verfügt über Eigenschaften, die im Kontext des Mechanismus selbst aussagekräftige Namen haben.

Auf der anderen Seite bietet die Verwendung von benutzerdefinierten Datenobjekten eine bessere Darstellung Ihrer Daten und viele Vorteile, einschließlich Lesbarkeit und Erweiterbarkeit. Es gibt nichts, was Sie daran hindern könnten, vorhandenen Code in benutzerdefinierten Objekten wie von Sign vorgeschlagen wiederzuverwenden , aber ich würde versuchen, die umgebrochene Klasse auszublenden und Abstraktionslecks zu vermeiden , damit Sie die zugrunde liegende Implementierung zu einem späteren Zeitpunkt ändern können, ohne den Rest Ihres Codes zu beeinträchtigen .

Die eigentliche Frage lautet also : Stellen Sie Daten dar oder verwenden Sie die Daten in einem Mechanismus? (und ich würde nicht raten, diese Konzepte miteinander zu mischen).

Nach meiner Erfahrung gibt es nichts Schlimmeres, als zu sehen, dass ein Tupel [Zeichenfolge, Zeichenfolge] an eine Methode übergeben wird und nicht sofort weiß, was Item1 und Item2 sein sollen. Verwenden Sie Tuples am besten innerhalb von Methoden oder nur als private Mitglieder einer Klasse.

Ben
quelle