wake-up-neo.com

Wann zu verwenden: Tuple vs Class c # 7.0

Vor Tuples habe ich ein class und seine Variablen erstellt, dann ein Objekt aus dieser Klasse erstellt und dieses Objekt zum Rückgabetyp für einige Funktionen gemacht. 

Nun kann ich mit den Tupeln dasselbe tun und in c # 7.0 können wir für die Tupel-Eigenschaften verständliche Namen vergeben (vorher waren es item1, item2 usw.). 

Nun frage ich mich, wann sollte ich Tupel verwenden und wann sollte ich eine Klasse in c # 7.0 erstellen?

47
Madonna Remon

Da diese Antwort unter einigen Leuten hier einige Verwirrung stiftet, sollte ich klarstellen, dass sich alle Verweise auf "Tuple" hier auf den ValueTuple-Typ und die neuen Tuple-syntaktischen Zuckermerkmale von C # 7 beziehen und in keiner Weise auf den alte System.Tuple-Referenztypen.

Nun frage ich mich, wann sollte ich Tupel verwenden und wann sollte ich eine Klasse in c # 7.0 erstellen?

Nur Sie können diese Frage wirklich beantworten, da sie wirklich von Ihrem Code abhängt. 

Es gibt jedoch Richtlinien und Regeln, die Sie befolgen können, wenn Sie sich zwischen ihnen entscheiden:

Tupel sind Werte und werden nicht nach Verweis, sondern nach Wert kopiert.

Meistens sollte dies kein Problem sein. Wenn Sie jedoch Tupel großer Strukturen umgehen, kann dies Auswirkungen auf die Leistung haben. Ref-Locals/Returns können jedoch verwendet werden, um diese Leistungsprobleme zu umgehen.

Da es sich um Werte handelt, ändert das Ändern einer Kopie aus der Ferne die ursprüngliche Kopie nicht. Das ist eine gute Sache, könnte aber einige Leute herausholen.

Tupel-Elementnamen werden nicht beibehalten

Die Namen der Elemente werden vom Compiler verwendet und stehen (in den meisten Fällen) zur Laufzeit nicht zur Verfügung. Dies bedeutet, dass die Reflexion nicht dazu verwendet werden kann, ihre Namen herauszufinden. Auf sie kann nicht dynamisch zugegriffen werden, und sie können nicht in Rasierapparaten verwendet werden.

Dies ist auch ein wichtiger Aspekt bei APIs. Ein von einer Methode zurückgegebenes Tuple ist die Ausnahme von der Regel bezüglich der Erkennbarkeit des Namens nach der Kompilierung. Der Compiler fügt der Methode Attribute hinzu, die Informationen zu den Tuple-Namen enthalten. Dies bedeutet, dass Sie einen Tuple von einer öffentlichen Methode in einer Assembly sicher zurückgeben und auf dessen Namen in einer anderen Assembly zugreifen können.

Tuples sind leicht

Tupel sind viel einfacher zu schreiben als Typen, da sie weniger ausführlich sind und die Deklaration "inline" (dh am Verwendungsort deklariert) werden kann. Dies funktioniert gut, wenn Sie eine Methode deklarieren, die beispielsweise mehrere Werte zurückgibt.

Da sie jedoch an der Verwendungsstelle deklariert sind und MethodA aufgerufen wird, die MethodB aufruft und MethodC aufruft und jeweils ein Tuple zurückgibt, müssen Sie das Tuple in jeder Phase neu definieren. Es gibt ( noch ) keine Möglichkeit, einen Alias ​​eines Tuples zu erstellen und ihn über mehrere Methoden hinweg wiederzuverwenden.

Verwenden Sie einfach den gesunden Menschenverstand

Für alle Situationen, in denen Sie ein Tuple in Betracht ziehen, stellen Sie sich einfach die Frage: "Ein Tuple vereinfacht den Code hier". Wenn die Antwort "Ja" lautet, verwenden Sie eine. Und das ist letztlich die wichtigste Überlegung, ob ein Tupel oder eine benutzerdefinierte Klasse verwendet werden soll.

29
David Arno

Im Allgemeinen haben benannte Klassen eine gewisse Bedeutung für den Entwurf Ihres Systems. Sie sind auch ausführlicher zu schreiben. Beispielsweise haben Sie möglicherweise eine Klasse mit dem Namen 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 Bedeutung für das Design besteht und Sie lediglich ein leichtes Datenübertragungsobjekt (DTO) benötigen, um Informationen zu verschieben.

Verwenden Sie in der Regel eine vollständige Klasse, wenn für Ihre Klasse eine Dokumentation erforderlich ist, um zu beschreiben, wofür sie dient, oder wenn sie ein Verhalten bereitstellt. Wenn Sie nur temporären Speicher oder eine Art Gruppierung benötigen, verwenden Sie ein Tuple. Stellen Sie sich eine Situation vor, in der Sie mehr als einen Wert von einer asynchronen Methode zurückgeben möchten. Tuple soll dieses Problem lösen.

18
Gusdor

Verwenden Sie eine Klasse

Wenn es sich bei Ihren Objekten um Entitäten handelt, die in Ihrer gesamten Anwendung häufig verwendet werden und auch in einem permanenten Speicher wie einer relationalen Datenbank (SQL Server, MySQL, SQLite), einer NoSQL-Datenbank oder einem Cache (Redis, Azure DocumentDB) oder sogar einfach gespeichert werden Textdateien oder CSVs.

Ja, alles Beständige sollte eine eigene Klasse haben.

Einen Tupel verwenden

Wenn Ihre Objekte kurzlebig sind und keine besondere Bedeutung für Ihre Anwendung haben. Wenn Sie z. B. schnell ein Koordinatenpaar zurückgeben müssen, sollten Sie Folgendes verwenden:

(double Latitude, double Longitude) getCoordinates()
{
    return (144.93525, -98.356346);
}

als eine eigene Klasse definieren 

class Coordinates
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

Mit einem Tupel sparen Sie Zeit, da Sie mit new für eine solche einfache Operation Speicherplatz auf dem Heap reservieren müssen.

Ein anderes Mal, wenn ich Tupel für nützlich halte, 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. Was auch immer die Berechnungen sind, die Ergebnisse gehören nicht zu einer Klasse. Die Alternative wäre also die Verwendung von out-Variablen, aber ich glaube, Tupel sind ausdrucksvoller und führen zu einer besseren Lesbarkeit.

7
dimlucas

Ich möchte zunächst erwähnen, dass C # bereits Unterstützung für anonyme Typen hatte. Welches sind Referenztypen. Somit hatten Sie bereits eine geeignete Alternative zum Erstellen einer benannten Klasse.

Ein Vorteil einer benannten Klasse ist die einfachere Wiederverwendung (z. B. wenn Sie denselben Typ an mehreren Stellen benötigen) und das Dokument. Da der anonyme Typ anonym ist, können Sie nur Variablen eingeben, wenn Sie var verwenden können. Dies begrenzt die Kontexte, in denen anonyme Typen nützlich sind (beispielsweise können Sie sie nicht als Feldtypen, Rückgabetypen oder Parametertypen verwenden.) ).

Natürlich können Sie einige Einschränkungen anonymer Typen mit System.Tuple durchgehen. 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 anonyme Typen angesehen werden. Der erste Unterschied ist, dass es sich um Werttypen handelt. Dies bedeutet, dass diese Tupel einen Leistungsvorteil haben, solange sie im lokalen Bereich bleiben oder sich über den Stapel bewegen (was aufgrund seiner Einschränkung die übliche Verwendung anonymer Typen ist).

Der zweite Unterschied ist, dass mit der neuen Syntax Tupel an mehr Stellen als anonymen Typen angezeigt werden können. Wie Sie wissen, können Sie den Rückgabetyp mit ValueTuple mit syntaktischem Zucker definieren (bei der Verwendung von anonymen Typen mussten Sie object zurückgeben). 

Der dritte Unterschied ist, dass ValueTuple die Dekonstruktion sofort unterstützt. Zitieren Neues in C # 7.0 :

Eine andere Möglichkeit, Tupel zu konsumieren, besteht darin, sie zu dekonstruieren. Eine Dekonstruktionsdeklaration ist eine Syntax zum Aufteilen eines Tupels (oder eines anderen Werts) in seine Teile und zum Zuordnen dieser Teile zu neuen Variablen:

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");

Das können Sie auch mit benutzerdefinierten Typen, indem Sie eine Deconstruct-Methode hinzufügen.


Für abstrakt:

  • ValueTuple enthält syntaktischen Zucker in C # 7.0, der aus Gründen der Lesbarkeit in Betracht gezogen werden sollte.
  • ValueTuple ist ein Werttyp. Alle Vor- und Nachteile zwischen der Verwendung eines class und eines struct gelten.
  • ValueTuple kann explizit verwendet werden (mit oder ohne den syntaktischen Zucker), wodurch die Vielseitigkeit von System.Tuple unter Beibehaltung der benannten Mitglieder ermöglicht wird.
  • ValueTuple unterstützt die Dekonstruktion.

Da es sich bei diesem Muss um einen syntaktischen Zucker handelt, würde ich sagen, dass das stärkere Argument, ValueTuple zu wählen, dasselbe Argument ist, um ein struct zu wählen. Das wäre ideal für kleine, unveränderliche Typen, die meistens auf dem Stack leben (so dass Sie nicht viel Boxen und Unboxen haben).

Wenn Sie ValueTuple mit einer voll ausgereiften Struktur vergleichen und den syntaktischen Zucker berücksichtigen, würde ich vorschlagen, ValueTuple standardmäßig zu verwenden, es sei denn, Sie benötigen ein explizites Layout oder Sie 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 Bedeutung verleiht. Darüber hinaus können Sie einer struct- oder class-Deklaration Dokumentation hinzufügen, die das Verständnis erleichtert.

Alles in allem ist die Situation, in der ValueTuple wirklich glänzt, die Rückgabe mehrerer Werte aus einer Methode. In diesem Fall müssen keine neuen out-Parameter erstellt werden. Und die Dokumentation der verwendeten ValueTuple kann in der Dokumentation der Methode leben. Wenn Sie mit ValueTuple etwas anderes tun müssen (zum Beispiel Definieren von Erweiterungsmethoden), empfiehlt es sich, stattdessen einen benannten Typ zu erstellen.

3
Theraot

Tupel sollen mehrere Werte darstellen, z. B. wenn eine Methode mehrere Werte zurückgeben möchte. Die Tuple-Unterstützung in C # 7 verwendet System.ValueTuple<...>-Instanzen, um diese Menge von Werten 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.

2
Paulo Morgado

Im Allgemeinen möchten Sie eine Klasse haben, wenn Ihr Objekt woanders 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 einen Autohaus darstellt, nicht Tupel.

Andererseits möchten Sie manchmal nur einige Objekte aus einer Methode zurückgeben. Vielleicht stellen sie nichts Besonderes dar, es ist nur so, dass Sie sie in dieser speziellen Methode zusammen zurückgeben müssen. Manchmal, selbst wenn sie ein Konzept aus Ihrer Domäne darstellen (beispielsweise, dass Sie (Car, Store) zurückgeben, das als Sale-Objekt dargestellt werden kann), werden Sie sie tatsächlich nirgendwo verwenden. In solchen Fällen können Tupel verwendet werden.

Wenn Sie jetzt speziell von C # sprechen, sollten Sie noch etwas wissen. Der Tuple-Typ von C # 7 ist eigentlich ValueTuple, eine Struktur. Im Gegensatz zu Klassen, bei denen es sich um Referenztypen handelt, sind Strukturen Werttypen. Mehr darüber erfahren Sie unter msdn . Am wichtigsten ist jedoch, dass sie möglicherweise viel kopieren müssen. Seien Sie also vorsichtig.

2
andre_ss6

Tupel ist eine großartige Option, wenn Sie mehrere Werte (es können verschiedene Typen sein können) in einem Objekt kombinieren, ohne eine benutzerdefinierte Klasse zu erstellen. In diesem Fall wäre Tuple eine schnelle und perfekte Option.

1
Qasim Bataineh

Ich denke, das wird zu einer Frage, die oft gestellt wird. Derzeit gibt es keine "Best Practice", wann die neuen Wertetupel gegenüber einer Klasse verwendet werden sollen.

Es lohnt sich jedoch zu lesen was in früheren Gesprächen über die vorherigen Versionen von Tupeln im Vergleich zu einer Klasse auftauchte .

Meiner Meinung nach sollte der Wert Tuple nur minimal und mit maximal drei Werten verwendet werden. Ich denke, dass dies ein ausgewogenes Verhältnis zwischen "Werten ohne die Notwendigkeit einer Klasse" und "schrecklichen Werten" darstellt. Wenn Sie mehr als drei Werte zurückgeben müssen, erstellen Sie eine Klasse.

Ich würde auch niemals einen Tupel verwenden, um von einer öffentlichen API zurückzukehren, die die Verbraucher verwenden müssen. Verwenden Sie wieder eine Klasse.

Hier ist ein Code aus der realen Welt, den ich verwendet habe:

public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()

Sobald ich komplexere Daten zurückgeben möchte, mache ich eine Klasse.

0
user9993

Ich würde vermeiden, Tupel als Rückgabe öffentlicher Methoden zu verwenden. In solchen Fällen würde ich es vorziehen, eine Klasse oder Struktur zu definieren.

0