Welche Anforderung sollte das Tupel lösen?

94

Ich schaue mir die neue C # -Funktion von Tupeln an. Ich bin gespannt, welches Problem das Tupel lösen soll.

Wofür haben Sie Tupel in Ihren Apps verwendet?

Aktualisieren

Vielen Dank für die bisherigen Antworten. Lassen Sie mich sehen, ob ich die Dinge im Kopf habe. Ein gutes Beispiel für ein Tupel wurde als Koordinaten angegeben. Sieht das richtig aus?

var coords = Tuple.Create(geoLat,geoLong);

Verwenden Sie dann das Tupel wie folgt:

var myLatlng = new google.maps.LatLng("+ coords.Item1 + ", "+ coords.Item2 + ");

Ist das korrekt?

Chaddeus
quelle
4
Nun, ich kann mir zwei Antworten vorstellen ...
Noon Silk
13
Wenn das Konzept der "Koordinate" als Klasse Sinn macht, würde ich es zu einer Klasse machen. Verwenden Sie Tupel für Situationen, in denen es kein vernünftiges "Geschäftslogik" -Konzept für den Datensatz gibt, das die Erstellung eines Typs rechtfertigt.
Eric Lippert
13
Koordinaten sind kein gutes Beispiel für Tupel. Tupel in C # ist nur eine Ad-hoc-Lösung, wenn Sie (von einer Funktion) zwei Werte zurückgeben müssen. Anstatt einen Ergebnistyp plus einen Out-Parameter festzulegen, ist es eleganter, ein Tupel zurückzugeben. Auf diese Weise müssen Sie den zweiten Parameter nicht im Voraus deklarieren. Der Unterschied ist in C ++ noch deutlicher (Sie können das Ergebnis konstituieren, aber den Parameter out / ref nicht konstituieren).
Greenoldman
8
Weil Python es hatte und Python nichts haben kann, was C # nicht hat;)
Evan Plaice

Antworten:

117

Beim Schreiben von Programmen ist es äußerst üblich, eine Reihe von Werten logisch zu gruppieren, die nicht genügend Gemeinsamkeiten aufweisen, um die Erstellung einer Klasse zu rechtfertigen.

In vielen Programmiersprachen können Sie eine Reihe ansonsten nicht verwandter Werte logisch gruppieren, ohne einen Typ auf nur eine Weise zu erstellen:

void M(int foo, string bar, double blah)

Logischerweise ist dies genau das Gleiche wie eine Methode M, die ein Argument verwendet, das ein 3-Tupel von int, string, double ist. Aber ich hoffe du würdest eigentlich nicht machen:

class MArguments
{
   public int Foo { get; private set; } 
   ... etc

es sei denn, Dokumente hatten eine andere Bedeutung in der Geschäftslogik.

Das Konzept, "eine Reihe von ansonsten nicht zusammenhängenden Daten in einer Struktur zusammenzufassen, die leichter als eine Klasse ist", ist an vielen, vielen Stellen nützlich, nicht nur für formale Parameterlisten von Methoden. Dies ist nützlich, wenn eine Methode zwei Dinge zurückgeben muss oder wenn Sie ein Wörterbuch aus zwei Daten anstatt aus einem ableiten möchten, und so weiter.

Sprachen wie F #, die Tupeltypen von Haus aus unterstützen, bieten ihren Benutzern ein hohes Maß an Flexibilität. Sie sind ein äußerst nützlicher Satz von Datentypen. Das BCL-Team hat beschlossen, mit dem F # -Team zusammenzuarbeiten, um einen Tupeltyp für das Framework zu standardisieren, damit jede Sprache davon profitieren kann.

Derzeit gibt es jedoch keine Sprachunterstützung für Tupel in C #. Tupel sind nur ein anderer Datentyp wie jede andere Framework-Klasse. Es ist nichts Besonderes an ihnen. Wir erwägen, Tupel in hypothetischen zukünftigen Versionen von C # besser zu unterstützen. Wenn jemand irgendwelche Gedanken darüber hat, welche Art von Funktionen mit Tupeln Sie sehen möchten, würde ich sie gerne an das Designteam weitergeben. Realistische Szenarien überzeugen mehr als theoretische Überlegungen.

Eric Lippert
quelle
1
@MalcomTucker: Asychronie und Parallelität sind definitiv in unseren Köpfen als reichhaltige Bereiche, in denen Sprachwerkzeuge verwendet werden könnten, um echte Probleme zu lösen. Ich denke nicht, dass asynchrone Workflows im F # -Stil unbedingt am besten zu C # passen, aber sie sind definitiv inspirierend.
Eric Lippert
60
Während Tupel nützlich sind, ist ein großer Nachteil die Klarheit. Es ist schwer zu lesen und den Code zu verstehen , die sich bezieht Item1, Item2usw ... Wenn Tupeln jemals Sprachunterstützung in C # tun erreichen, wäre es wunderbar sein , ihre Mitglieder zu ermöglichen , in einer Weise benannt (oder zumindest aliased) werden , die Code ermöglicht , dass Anwendungen sie verständlicher zu sein. In einer solch hypthetischen Zukunft würde ich auch gerne Tupel mit geeigneter "Form" als rechtliche Parameter für Methoden sehen, die einzelne Parameter verwenden (und umgekehrt). So Tuple<int,string,bool>kann übergeben M(int,string,bool). Dies würde das Auswendiglernen von Methoden erheblich vereinfachen.
LBushkin
2
Diese "Tupel" sind im Grunde alle anonymenpublic Zugriffstypen mit unbenannten Mitgliedern . Ich möchte lieber, dass der Programmierer eine mit benannten Mitgliedern schreibt und diese zurückgibt, als sich mit einer zu befassen , die mir nichts über die Semantik seiner Mitglieder sagt. structstructtuple
Bobobobo
1
@ Hi-Angel: Es geht nicht darum, darüber zu streiten, was als Tupel zählt und was nicht, sondern darum, welche Funktionen moderne Branchenentwickler nutzen könnten, um ihre Produktivität zu steigern . LBushkin drückt eine Feature-Anfrage aus, die das Designteam ständig hört: den Wunsch, einen "Datensatztyp" zu erstellen, ohne die Mühe und die Kosten, eine ganze Klasse zu erstellen, nur um ein paar Felder zusammenzuhalten. C # verfügt bereits über diese Funktion für Methoden. Es heißt "Parameterliste".
Eric Lippert
2
@ Hi-Angel: Sie würden wahrscheinlich nicht das Argument "Wenn Sie auf Ihre Methodenparameter über den Namen zugreifen möchten, sollten Sie eine separate Klasse erstellen" machen, weil das extrem schwer zu sein scheint, aber es scheint nur schwer, weil wir es gewohnt sind, das zu haben Fähigkeit, beim Aufrufen einer Methode sofort eine Reihe beliebiger Variablen mit beliebigen Typen und Namen zu verknüpfen . Stellen Sie sich vor, keine Sprache hätte diese Funktion. Jede Methode konnte nur ein einziges Argument annehmen, und wenn Sie zwei übergeben möchten, müssen Sie einen Typ erstellen und eine Instanz davon übergeben.
Eric Lippert
22

Tupel bieten eine unveränderliche Implementierung einer Sammlung

Abgesehen von den üblichen Verwendungen von Tupeln:

  • gemeinsame Werte zu gruppieren, ohne eine Klasse erstellen zu müssen
  • um mehrere Werte von einer Funktion / Methode zurückzugeben
  • etc...

Unveränderliche Objekte sind von Natur aus threadsicher:

Unveränderliche Objekte können in Multithread-Anwendungen nützlich sein. Mehrere Threads können auf Daten einwirken, die durch unveränderliche Objekte dargestellt werden, ohne dass die Daten von anderen Threads geändert werden müssen. Unveränderliche Objekte gelten daher als threadsicherer als veränderbare Objekte.

Aus "Unveränderliches Objekt" auf Wikipedia

Evan Scholle
quelle
Vielen Dank, dass Sie der Frage zusätzliche Informationen hinzugefügt haben. Sehr geschätzt.
Chaddeus
Wenn Sie eine unveränderliche Sammlung wünschen, sollten Sie eine unveränderliche Sammlung verwenden, kein Tupel. Der Unterschied besteht darin, dass Sie für die meisten unveränderlichen Sammlungen nicht die Anzahl der Elemente zur Kompilierungszeit angeben müssen
BlueRaja - Danny Pflughoeft
@ BlueRaja-DannyPflughoeft Als ich diese Antwort schrieb, waren die unveränderlichen C # -Sammlungsklassen in der BCL noch nicht verfügbar. Ich schätze, ich werde 'Sammlung' in 'Objekt' ändern, da MS die Tupel-Implementierung in C #
Evan Plaice am
12

Es bietet eine Alternative zu refoder outwenn Sie eine Methode haben, die als Teil ihrer Antwort mehrere neue Objekte zurückgeben muss.

Sie können auch einen integrierten Typ als Rückgabetyp verwenden, wenn Sie nur zwei oder drei vorhandene Typen mischen müssen und nicht nur für diese Kombination eine Klasse / Struktur hinzufügen müssen. (Wünschten Sie sich jemals, eine Funktion könnte einen anonymen Typ zurückgeben? Dies ist eine teilweise Antwort auf diese Situation.)

Toby
quelle
Verdammt. Nett. Ich wünschte, ich hätte vor ein paar Jahren überprüft, was Tupel sind;).
Sabiland
10

Es ist oft hilfreich, einen "Paar" -Typ zu haben, der nur in schnellen Situationen verwendet wird (z. B. wenn zwei Werte von einer Methode zurückgegeben werden). Tupel sind ein zentraler Bestandteil funktionaler Sprachen wie F #, und C # hat sie auf dem Weg aufgenommen.

Stephen Cleary
quelle
System.Web.UI hat eine Pair-Klasse msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx .
StingyJack
5

Sehr nützlich, um zwei Werte von einer Funktion zurückzugeben

Keith Nicholas
quelle
1
Vielleicht zum Umschließen eines anonymen Typs?
Bloparod
1
Ich sehe sie irgendwie als anonyme Strukturen
Matt Evans
4

Persönlich finde ich, dass Tupel ein iterativer Teil der Entwicklung sind, wenn Sie sich in einem Untersuchungszyklus befinden oder einfach nur "spielen". Da ein Tupel generisch ist, denke ich eher daran, wenn ich mit generischen Parametern arbeite - insbesondere, wenn ich einen generischen Code entwickeln möchte, und ich beginne am Codeende, anstatt mich zu fragen, wie mir dieser Aufruf gefallen würde sehen?".

Sehr oft stelle ich fest, dass die Sammlung, die die Tupelformulare bilden, Teil einer Liste wird, und wenn ich auf Liste> starre, drückt dies nicht wirklich die Absicht der Liste aus oder wie sie funktioniert. Ich "lebe" oft damit, möchte aber die Liste manipulieren und einen Wert ändern. An diesem Punkt möchte ich nicht unbedingt ein neues Tupel dafür erstellen, daher muss ich meine eigene Klasse oder Struktur erstellen um es zu halten, damit ich Manipulationscode hinzufügen kann.

Natürlich gibt es immer Erweiterungsmethoden - aber oft möchten Sie diesen zusätzlichen Code nicht auf generische Implementierungen erweitern.

Es gab Zeiten, in denen ich Daten als Tupel ausdrücken wollte und keine Tupel zur Verfügung hatte. (VS2008) In diesem Fall habe ich gerade meine eigene Tuple-Klasse erstellt - und ich mache sie nicht threadsicher (unveränderlich).

Ich bin also der Meinung, dass Tuples faul programmieren, wenn ein Typname verloren geht, der seinen Zweck beschreibt. Der andere Aufwand besteht darin, dass Sie die Signatur des Tupels dort deklarieren müssen, wo es als Parameter verwendet wird. Nach einer Reihe von Methoden, die aufgebläht aussehen, haben Sie vielleicht das Gefühl, dass es sich lohnt, eine Klasse zu erstellen, da sie die Methodensignaturen bereinigt.

Ich beginne damit, dass die Klasse ein öffentliches Mitglied der Klasse ist, in der Sie bereits arbeiten. In dem Moment, in dem sie über eine Sammlung von Werten hinausgeht, erhält sie eine eigene Datei und verschiebt sie aus der enthaltenen Klasse.

Rückblickend glaube ich, dass ich Tupel benutze, wenn ich nicht losgehen und eine Klasse schreiben möchte und nur darüber nachdenken möchte, was ich gerade schreibe. Dies bedeutet, dass sich die Signatur des Tupels im Text eine halbe Stunde lang stark ändern kann, während ich herausfinde, welche Daten ich für diese Methode benötige und wie sie die zurückgegebenen Werte zurückgibt.

Wenn ich die Möglichkeit habe, Code umzugestalten, frage ich oft nach dem Platz eines Tupels darin.

Simon Miller
quelle
3

Alte Frage seit 2010 und jetzt im Jahr 2017 ändert sich Dotnet und wird schlauer.

C # 7 führt die Sprachunterstützung für Tupel ein, die semantische Namen für die Felder eines Tupels mithilfe neuer, effizienterer Tupeltypen ermöglicht.

In vs 2017 und .Net 4.7 (oder bei der Installation des Nuget-Pakets System.ValueTuple) können Sie ein Tupel auf sehr effiziente und einfache Weise erstellen / verwenden:

     var person = (Id:"123", Name:"john"); //create tuble with two items
     Console.WriteLine($"{person.Id} name:{person.Name}") //access its fields

Rückgabe von mehr als einem Wert aus einer Methode:

    public (double sum, double average) ComputeSumAndAverage(List<double> list)
    {
       var sum= list.Sum();
        var average = sum/list.Count;
        return (sum, average);
    }

    How to use:

        var list=new List<double>{1,2,3};
        var result = ComputeSumAndAverage(list);
        Console.WriteLine($"Sum={result.sum} Average={result.average}");    

Weitere Informationen finden Sie unter: https://docs.microsoft.com/en-us/dotnet/csharp/tuples

M. Hassan
quelle
1

Ein Tupel wird häufig verwendet, um mehrere Werte von Funktionen zurückzugeben, wenn Sie keinen bestimmten Typ erstellen möchten. Wenn Sie mit Python vertraut sind, hat Python dies schon lange.

Randy Minder
quelle
1

Rückgabe von mehr als einem Wert von einer Funktion. getCoordinates () ist nicht sehr nützlich, wenn es nur x oder y oder z zurückgibt, aber es scheint auch ziemlich schwer zu sein, eine vollständige Klasse und ein Objekt für drei Ints zu erstellen.

Dean J.
quelle
1

Eine häufige Verwendung könnte darin bestehen, das Erstellen von Klassen / Strukturen zu vermeiden, die nur zwei Felder enthalten. Stattdessen erstellen Sie ein Tupel (oder vorerst ein KeyValuePair). Nützlich als Rückgabewert, vermeiden Sie es, N Parameter auszugeben ...

user347594
quelle
0

Ich finde, dass das KeyValuePair in C # aktualisiert wird, um die Schlüsselwertpaare in einem Wörterbuch zu durchlaufen.

Jubal
quelle
KeyValuePairkann als spezielle Problemumgehung angesehen werden. In Python gibt das Durchlaufen eines dictJust nur Tupel (Schlüssel, Wert) zurück.
dan04
Ja, wie Python. :-) Mir war nicht bewusst, dass KeyValuePair in c # kein Tupel war?
Jubal
0

Es ist sehr hilfreich bei der Rückgabe von Werten aus Funktionen. Wir können mehrere Werte zurück haben und dies ist in einigen Szenarien eine ziemliche Ersparnis.

Akshat
quelle
0

Ich bin auf diesen Leistungsbenchmark zwischen Tupeln und Schlüssel-Wert-Paaren gestoßen, und wahrscheinlich werden Sie ihn interessant finden. Zusammenfassend heißt es, dass Tuple einen Vorteil hat, weil es eine Klasse ist, daher wird es im Heap und nicht im Stapel gespeichert, und wenn es als Argument herumgereicht wird, ist sein Zeiger das einzige, was geht. Da KeyValuePair jedoch eine Struktur ist, ist die Zuweisung schneller, bei Verwendung jedoch langsamer.

http://www.dotnetperls.com/tuple-keyvaluepair

Ognyan Dimitrov
quelle