Vor Tuples habe ich ein class
und seine Variablen erstellt und dann ein Objekt aus dieser Klasse erstellt und dieses Objekt zum Rückgabetyp für einige Funktionen gemacht.
Jetzt mit den Tupeln kann ich das gleiche tun und in c # 7.0 wir verständliche Namen für Tupeln Eigenschaften zuordnen können (vorher war es item1
, item2
, etc ..)
Jetzt frage ich mich also, wann ich es verwenden soll und wann ich eine Klasse in c # 7.0 erstellen soll.
c#
tuples
c#-7.0
valuetuple
Madonna Remon
quelle
quelle
Antworten:
Da diese Antwort bei einigen Leuten hier einige Verwirrung stiftet, sollte ich klarstellen, dass sich - gemäß der Frage - alle Verweise auf "Tupel" hier auf den
ValueTuple
Typ und die neuen syntaktischen Tupelzuckermerkmale von C # 7 beziehen und sich in keiner Weise auf die alten beziehenSystem.Tuple
Referenztypen.Nur Sie können diese Frage wirklich beantworten, da sie wirklich von Ihrem Code abhängt.
Es gibt jedoch Richtlinien und Regeln, die Sie bei der Auswahl befolgen können:
Tupel sind Werte und werden daher eher nach Wert als nach Referenz kopiert.
Meistens sollte dies kein Problem sein. Wenn Sie jedoch Tupel großer Strukturen herumgeben, kann dies Auswirkungen auf die Leistung haben. Ref-Einheimische / Retouren können jedoch verwendet werden, um diese Leistungsprobleme zu umgehen.
Da es sich um Werte handelt, wird durch Ändern einer Kopie aus der Ferne die Originalkopie nicht geändert. Dies ist eine gute Sache, könnte aber einige Leute herausholen.
Tupelelementnamen werden nicht beibehalten
Die Namen der Elemente werden vom Compiler verwendet und sind (in den meisten Fällen) zur Laufzeit nicht verfügbar. Dies bedeutet, dass Reflexion nicht verwendet werden kann, um ihre Namen zu entdecken. Auf sie kann nicht dynamisch zugegriffen werden, und sie können nicht in Rasiereransichten verwendet werden.
Auch dies ist eine wichtige Überlegung bei APIs. Ein von einer Methode zurückgegebenes Tupel ist die Ausnahme von der Regel bezüglich der Auffindbarkeit von Namen nach der Kompilierung. Der Compiler fügt der Methode Attribute hinzu, die Informationen zu den Tupelnamen enthalten. Dies bedeutet, dass Sie ein Tupel aus einer öffentlichen Methode in einer Assembly sicher zurückgeben und in einer anderen auf seine Namen zugreifen können.
Tupel sind leicht
Tupel sind viel einfacher zu schreiben als Typen, da sie weniger ausführlich sind und die Deklaration "inline" sein kann (dh am Verwendungsort deklariert wird). Dies funktioniert gut, wenn Sie beispielsweise eine Methode deklarieren, die mehrere Werte zurückgibt.
Da sie jedoch am Verwendungsort deklariert sind , müssen Sie das Tupel in jeder Phase neu definieren , wenn Sie
MethodA
diese Aufrufe haben ,MethodB
die aufrufenMethodC
und jeweils ein Tupel zurückgeben. Es gibt ( noch ) keine Möglichkeit, einen Alias eines Tupels zu erstellen und ihn über mehrere Methoden hinweg wiederzuverwenden.Verwenden Sie einfach den gesunden Menschenverstand
Für jede Situation, in der Sie ein Tupel verwenden könnten: Stellen Sie sich einfach die Frage: "Wird ein Tupel den Code hier vereinfachen?". Wenn die Antwort "Ja" lautet, verwenden Sie eine. Und das ist letztendlich die wichtigste Überlegung, ob ein Tupel oder eine benutzerdefinierte Klasse verwendet werden soll.
quelle
ValueTuple
ist Werttyp.Tuple
ist Referenztyp. So einfach wirdTuple
nicht als Referenz kopiert. Sie sollten es fett erwähnen, weil es gerade verwirrend istIm Allgemeinen haben benannte Klassen eine gewisse Bedeutung für das Design Ihres Systems. Sie sind auch ausführlicher zu schreiben. Beispielsweise kann eine Klasse aufgerufen werden
MediaFileOpener
. Für das Design ist es wichtig, dass wir wissen, was diese Klasse tut - wir arbeiten mit Mediendateien!Anonyme Typen und Tupel werden verwendet, wenn keine Entwurfsbedeutung vorliegt und Sie lediglich ein leichtes Datenübertragungsobjekt (DTO) zum Verschieben von Informationen benötigen.
Verwenden Sie in der Regel eine vollständige Klasse, wenn für Ihre Klasse eine Dokumentation erforderlich ist, um zu beschreiben, wofür sie gedacht ist, oder wenn ein Verhalten vorliegt. Wenn Sie nur temporären Speicher oder eine Gruppierung benötigen, verwenden Sie ein Tupel. Stellen Sie sich eine Situation vor, in der Sie mehr als einen Wert von einer asynchronen Methode zurückgeben möchten. Tuple wurde entwickelt, um dieses Problem zu lösen.
quelle
Verwenden Sie eine Klasse
Wenn Ihre Objekte Entitäten sind, die in Ihrer gesamten Anwendung weit verbreitet sind und auch in einem dauerhaften Speicher wie einer relationalen Datenbank (SQL Server, MySQL, SQLite), einer NoSQL-Datenbank oder einem Cache (Redis, Azure DocumentDB) oder sogar einfach gespeichert sind Textdateien oder CSVs.
Also ja, alles, was hartnäckig ist, sollte eine eigene Klasse haben.
Verwenden Sie ein Tupel
Wenn Ihre Objekte nur von kurzer Dauer sind und keine besondere Bedeutung für Ihre Anwendung haben. Wenn Sie beispielsweise schnell ein Koordinatenpaar zurückgeben müssen, ist es besser, Folgendes zu haben:
(double Latitude, double Longitude) getCoordinates() { return (144.93525, -98.356346); }
als eine separate Klasse definieren
class Coordinates { public double Latitude { get; set; } public double Longitude { get; set; } }
Ein Tupel spart Ihnen Zeit, da Sie
new
für eine so einfache Operation keinen Speicher auf dem Heap zuweisen müssen .Ein anderes Mal, wenn ich Tupel nützlich finde, ist das Ausführen mehrerer mathematischer Operationen an einigen Operanden
(double Result1, double Result2, double Result3) performCalculations(int operand1, int operand 2)
In diesem Fall ist es nicht sinnvoll, eine Klasse zu definieren. Unabhängig von den Berechnungen gehören die Ergebnisse nicht zu einer Klasse. Die Alternative wäre also,
out
Variablen zu verwenden , aber ich glaube, Tupel sind aussagekräftiger und führen zu einer verbesserten Lesbarkeit.quelle
Coordinates
alsstruct
anstelle vonclass
.ToString
, um eine lesbare Ausgabe bereitzustellen (im Übrigen funktioniert der Standard-ToString des Tupels hier bereits gut), und auch überladenEquals
/GetHashCode
für Gleichheitsvergleiche.Im Allgemeinen möchten Sie eine Klasse haben, wenn Ihr Objekt an einem anderen Ort verwendet wird oder wenn es ein reales Objekt oder ein Konzept in Ihrer Domäne darstellt. Sie werden wahrscheinlich eine Klasse erstellen, die ein Auto oder ein Autohaus darstellt, keine Tupel.
Andererseits möchten Sie manchmal nur ein paar Objekte von einer Methode zurückgeben. Vielleicht stellen sie nichts Besonderes dar, es ist nur so, dass Sie sie in dieser bestimmten Methode zusammengeben müssen. Selbst wenn sie ein Konzept aus Ihrer Domain darstellen (sagen wir, Sie kehren zurück
(Car, Store)
, das alsSale
Objekt dargestellt werden könnte), werden Sie sie manchmal nirgendwo verwenden - Sie verschieben nur Daten. In diesen Fällen ist es in Ordnung, Tupel zu verwenden.Wenn Sie nun speziell über C # sprechen, sollten Sie noch etwas wissen. Der Tupeltyp von C # 7 ist eigentlich der
ValueTuple
, der eine Struktur ist. Im Gegensatz zu Klassen, die Referenztypen sind, sind Strukturen Werttypen. Sie können mehr darüber auf msdn lesen . Am wichtigsten ist, dass Sie wissen, dass sie möglicherweise viel kopieren. Seien Sie also vorsichtig.quelle
Ich denke, das wird eine Frage, die oft gestellt wird. Derzeit gibt es keine "Best Practice" für die Verwendung der neuen Wertetupel gegenüber einer Klasse.
Es lohnt sich jedoch zu lesen, was in früheren Gesprächen über die vorherigen Versionen von Tupeln gegen eine Klasse aufgetaucht ist .
Meiner Meinung nach sollte das Wertetupel nur minimal und mit maximal drei Werten verwendet werden. Ich denke, dies schafft eine gute Balance zwischen "Rückgabe einiger Werte ohne die Notwendigkeit einer Klasse" und "schrecklichem Durcheinander von Werten". Wenn Sie mehr als drei Werte zurückgeben müssen, erstellen Sie eine Klasse.
Ich würde auch niemals ein Tupel verwenden, um von einer öffentlich zugänglichen API zurückzukehren, die Verbraucher verwenden müssen. Verwenden Sie einfach eine Klasse.
Hier ist ein realer Code, den ich verwendet habe:
public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()
Sobald ich komplexere Daten zurückgeben möchte, erstelle ich eine Klasse.
quelle
Ich möchte zunächst erwähnen, dass C # bereits Unterstützung für anonyme Typen hatte . Welches sind Referenztypen. Sie hatten also bereits eine geeignete Alternative zum Erstellen einer benannten Klasse.
Ein Vorteil einer benannten Klasse besteht darin, dass sie leichter wiederverwendet werden kann (z. B. wenn Sie denselben Typ an mehreren Stellen benötigen) und dokumentiert. Da der anonyme Typ anonym ist, können Sie nur dann Variablen eingeben, wenn Sie ihn verwenden können. Dies
var
schränkt die Kontexte ein, in denen anonyme Typen nützlich sind (z. B. können Sie sie nicht als Feldtypen, Rückgabetypen oder Parametertypen verwenden). .Natürlich können Sie einige der Einschränkungen anonymer Typen mit verwenden
System.Tuple
. Dies ist auch ein Referenztyp, den Sie explizit verwenden können. Der Nachteil ist, dass es keine benutzerdefinierten Namen für die Mitglieder gibt.C # 7-Tupel (
ValueTuple
) können als ähnlich wie anonyme Typen angesehen werden. Der erste Unterschied besteht darin, dass es sich um Werttypen handelt. Dies bedeutet, dass diese Tupel einen Leistungsvorteil haben, solange sie sich im lokalen Bereich befinden oder sich über den Stapel bewegen (was aufgrund seiner Einschränkung die übliche Verwendung anonymer Typen ist).Der zweite Unterschied besteht darin, dass die neue Syntax ermöglicht, dass Tupel an mehr Stellen als anonyme Typen angezeigt werden. Wie Sie wissen, haben Sie syntaktischen Zucker, um den Rückgabetyp zu definieren
ValueTuple
(während Sie anonyme Typen verwenden, die Sie zurückgeben musstenobject
).Der dritte Unterschied besteht darin, dass die
ValueTuple
sofortige Dekonstruktion unterstützt wird. So zitieren Sie die Neuerungen in C # 7.0 :Dies können Sie auch mit benutzerdefinierten Typen tun, indem Sie eine Deconstruct-Methode hinzufügen .
Für Zusammenfassung:
ValueTuple
hat syntaktischen Zucker in C # 7.0, der für die Lesbarkeit berücksichtigt werden sollte.ValueTuple
ist ein Werttyp. Alle Vor- und Nachteile zwischen der Verwendung von aclass
und astruct
gelten.ValueTuple
kann explizit verwendet werden (mit oder ohne syntaktischen Zucker), so dass es die Vielseitigkeit hat,System.Tuple
während benannte Mitglieder erhalten bleiben .ValueTuple
unterstützt die Dekonstruktion.Angesichts der Tatsache, dass es sich bei dem Muss um syntaktischen Zucker handelt, würde ich sagen, dass das stärkere Argument für die Wahl
ValueTuple
dasselbe Argument für die Wahl von a iststruct
. Das wäre ideal für kleine, unveränderliche Typen, die meistens auf dem Stapel leben (so dass Sie nicht viel Boxen und Unboxen haben).Im Vergleich zu
ValueTuple
einer vollständigen Struktur würde ich unter Berücksichtigung des syntaktischen Zuckers empfehlen, dieseValueTuple
standardmäßig zu verwenden, es sei denn, Sie benötigen ein explizites Layout oder müssen Methoden hinzufügen.Ich möchte auch sagen, dass der syntaktische Zucker die Lesbarkeit nicht unbedingt verbessert. Der Hauptgrund ist, dass Sie den Typ nicht benennen und der Name des Typs dem Code eine Bedeutung verleiht. Darüber hinaus können Sie einer
struct
oder einerclass
Erklärung eine Dokumentation hinzufügen , die das Verständnis erleichtert.Alles in allem gibt die Situation, in der
ValueTuple
wirklich glänzt, mehrere Werte von einer Methode zurück. In diesem Fall müssen keine neuenout
Parameter erstellt werden. Und die Dokumentation derValueTuple
verwendeten kann in der Dokumentation der Methode leben. Wenn Sie feststellen, dass Sie etwas anderes mit dem tun müssenValueTuple
(z. B. das Definieren von Erweiterungsmethoden), würde ich empfehlen, stattdessen einen benannten Typ zu erstellen.quelle
Tupel sollen mehrere Werte darstellen, beispielsweise wenn eine Methode mehrere Werte zurückgeben möchte. Die Tupelunterstützung in C # 7 verwendet
System.ValueTuple<...>
Instanzen, um diesen Wertesatz darzustellen. Die Namen dieser Werte sind nur in dem Kontext gültig, in dem sie verwendet werden, und werden nicht erzwungen.Klassen sollen einen einzelnen Wert mit mehreren Attributen darstellen.
quelle
Tupel ist eine großartige Option, wenn Sie mehrere Werte (können unterschiedliche Typen sein) zu einem Objekt kombinieren möchten, ohne eine benutzerdefinierte Klasse zu erstellen. In diesem Fall wäre Tuple eine schnelle und perfekte Option.
quelle
Ich würde es vermeiden, Tupel als öffentliche Methode vom Rückgabetyp zu verwenden. In solchen Fällen würde ich lieber eine Klasse oder Struktur definieren.
quelle