Ist die Verwendung von .NET 4.0-Tupeln in meinem C # -Code eine schlechte Entwurfsentscheidung?

166

Mit der Hinzufügung der Tuple- Klasse in .net 4 habe ich versucht zu entscheiden, ob die Verwendung in meinem Design eine schlechte Wahl ist oder nicht. So wie ich es sehe, kann ein Tupel eine Abkürzung zum Schreiben einer Ergebnisklasse sein (ich bin sicher, dass es auch andere Verwendungszwecke gibt).

Also das:

public class ResultType
{
    public string StringValue { get; set; }
    public int IntValue { get; set; }
}

public ResultType GetAClassedValue()
{
    //..Do Some Stuff
    ResultType result = new ResultType { StringValue = "A String", IntValue = 2 };
    return result;
}

Ist gleichbedeutend damit:

public Tuple<string, int> GetATupledValue()
{
    //...Do Some stuff
    Tuple<string, int> result = new Tuple<string, int>("A String", 2);
    return result;
}

Beiseite die Möglichkeit so einstellen , dass ich den Punkt von Tupeln bin fehlt, ist das Beispiel mit einem Tuple ein schlechtes Design Wahl? Für mich scheint es weniger Unordnung zu sein, aber nicht so selbstdokumentierend und sauber. Das bedeutet, dass mit dem Typ ResultTypespäter sehr klar wird, was jeder Teil der Klasse bedeutet, aber Sie müssen zusätzlichen Code pflegen. Mit dem müssen Tuple<string, int>Sie nachschlagen und herausfinden, was jedes Itemdarstellt, aber Sie schreiben und pflegen weniger Code.

Jede Erfahrung, die Sie mit dieser Wahl gemacht haben, wäre sehr dankbar.

Jason Webb
quelle
16
@ Boltbait: weil Tupel aus der Mengenlehre stammt en.wikipedia.org/wiki/Tuple
Matthew Whited
16
@BoltBait: Ein "Tupel" ist eine geordnete Menge von Werten und hat nicht unbedingt etwas mit Zeilen in einer Datenbank zu tun.
FrustratedWithFormsDesigner
19
Tupel in c # wären noch schöner, wenn es eine gute Aktion zum Auspacken von Tupeln gäbe :)
Justin
4
@ John Saunders Meine Frage bezieht sich speziell auf Entwurfsentscheidungen in c #. Ich verwende keine anderen .net-Sprachen, daher bin ich mir nicht sicher, wie sich Tupel auf sie auswirken. Also habe ich es mit c # markiert. Es schien mir vernünftig, aber die Änderung zu .net ist auch in Ordnung, denke ich.
Jason Webb
13
@ John Saunders: Ich bin mir ziemlich sicher, dass er nach Entwurfsentscheidungen fragt, die sich auf die Verwendung oder Nichtverwendung von Tupeln in einer Anwendung beziehen, die nur in C # geschrieben ist. Ich glaube nicht, dass er nach Designentscheidungen fragt, die zur Erstellung der C # -Sprache geführt haben.
Brett Widmeier

Antworten:

163

Tupel sind großartig, wenn Sie sowohl das Erstellen als auch das Verwenden steuern. Sie können den Kontext beibehalten, der für das Verständnis der Tupel unerlässlich ist.

Auf einer öffentlichen API sind sie jedoch weniger effektiv. Der Verbraucher (nicht Sie) muss die Dokumentation entweder erraten oder nachschlagen, insbesondere für Dinge wie Tuple<int, int>.

Ich würde sie für private / interne Mitglieder verwenden, aber Ergebnisklassen für öffentliche / geschützte Mitglieder.

Diese Antwort enthält auch einige Informationen.

Bryan Watts
quelle
19
Der Unterschied zwischen öffentlich / privat ist wirklich wichtig, danke, dass Sie ihn angesprochen haben. Das Zurückgeben von Tupeln in Ihrer öffentlichen API ist eine wirklich schlechte Idee, aber intern ist es in Ordnung , wenn die Dinge eng miteinander verbunden sind (aber das ist in Ordnung ).
Clinton Pierce
36
Eng gekoppelt oder eng gekuppelt? ;)
contactmatt
Könnte eine entsprechende Dokumentation hier nicht hilfreich sein? Zum Beispiel hat Scala StringOps(im Grunde eine Klasse von "Erweiterungsmethoden" für Java String) eine Methode parition(Char => Boolean): (String, String)(nimmt Prädikat auf, gibt ein Tupel mit 2 Zeichenfolgen zurück). Es ist offensichtlich, was die Ausgabe ist (offensichtlich wird eines die Zeichen sein, für die das Prädikat wahr war, und das andere, für das es falsch war, aber es ist nicht klar, welches welches ist). Es ist die Dokumentation, die dies klärt (und der Mustervergleich kann verwendet werden, um bei der Verwendung klar zu machen, welches welches ist).
Kat
@ Mike: Das macht es schwierig, richtig und leicht falsch zu liegen, das Markenzeichen einer API, die irgendwo jemandem Schmerzen bereitet :-)
Bryan Watts
79

So wie ich es sehe, ist ein Tupel eine Abkürzung zum Schreiben einer Ergebnisklasse (ich bin sicher, dass es auch andere Verwendungszwecke gibt).

Es gibt in der Tat andere wertvolle Verwendungszwecke fürTuple<> - die meisten von ihnen beinhalten das Abstrahieren der Semantik einer bestimmten Gruppe von Typen, die eine ähnliche Struktur aufweisen, und das einfache Behandeln dieser Werte als geordnete Menge von Werten. In allen Fällen besteht ein Vorteil von Tupeln darin, dass sie vermeiden, Ihren Namespace mit Nur-Daten-Klassen zu überladen, die Eigenschaften, aber keine Methoden verfügbar machen.

Hier ist ein Beispiel für eine sinnvolle Verwendung für Tuple<>:

var opponents = new Tuple<Player,Player>( playerBob, playerSam );

Im obigen Beispiel möchten wir ein Paar von Gegnern darstellen. Ein Tupel ist eine bequeme Möglichkeit, diese Instanzen zu koppeln, ohne eine neue Klasse erstellen zu müssen. Hier ist ein weiteres Beispiel:

var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );

Eine Pokerhand kann nur als ein Kartensatz betrachtet werden - und Tupel (kann) eine vernünftige Art sein, dieses Konzept auszudrücken.

Abgesehen von der Möglichkeit, dass mir der Punkt Tupel fehlt, ist das Beispiel mit einem Tupel eine schlechte Wahl für das Design?

Die Rückgabe stark typisierter Tuple<>Instanzen als Teil einer öffentlichen API für einen öffentlichen Typ ist selten eine gute Idee. Wie Sie selbst erkennen, müssen die beteiligten Parteien (Bibliotheksautor, Bibliotheksbenutzer) bei Tupeln im Voraus den Zweck und die Interpretation der verwendeten Tupeltypen vereinbaren. Es ist schwierig genug, APIs zu erstellen, die intuitiv und klar sind und deren Verwendung Tuple<>nur die Absicht und das Verhalten der API verdeckt.

Anonyme Typen sind ebenfalls eine Art Tupel . Sie sind jedoch stark typisiert und ermöglichen es Ihnen, klare, informative Namen für die zum Typ gehörenden Eigenschaften anzugeben. Anonyme Typen sind jedoch für verschiedene Methoden schwierig zu verwenden. Sie wurden hauptsächlich hinzugefügt, um Technologien wie LINQ zu unterstützen, bei denen Projektionen Typen erzeugen, denen wir normalerweise keine Namen zuweisen möchten. (Ja, ich weiß, dass anonyme Typen mit denselben Typen und benannten Eigenschaften vom Compiler konsolidiert werden.)

Meine Faustregel lautet: Wenn Sie es von Ihrer öffentlichen Schnittstelle zurückgeben, machen Sie es zu einem benannten Typ .

Meine andere Faustregel für die Verwendung von Tupeln lautet: Argumente für Namensmethoden und lokale Variablen vom Typ Tuple<>so klar wie möglich - lassen Sie den Namen die Bedeutung der Beziehungen zwischen Elementen des Tupels darstellen. Denken Sie an mein var opponents = ...Beispiel.

Hier ist ein Beispiel für einen realen Fall, in dem ich vermieden habe Tuple<>, einen Nur-Daten-Typ für die Verwendung nur in meiner eigenen Assembly zu deklarieren . Die Situation beinhaltet die Tatsache, dass es bei Verwendung von generischen Wörterbüchern mit anonymen Typen schwierig wird, die TryGetValue()Methode zum Suchen von Elementen im Wörterbuch zu verwenden, da für die Methode ein outParameter erforderlich ist, der nicht benannt werden kann:

public static class DictionaryExt 
{
    // helper method that allows compiler to provide type inference
    // when attempting to locate optionally existent items in a dictionary
    public static Tuple<TValue,bool> Find<TKey,TValue>( 
        this IDictionary<TKey,TValue> dict, TKey keyToFind ) 
    {
        TValue foundValue = default(TValue);
        bool wasFound = dict.TryGetValue( keyToFind, out foundValue );
        return Tuple.Create( foundValue, wasFound );
    }
}

public class Program
{
    public static void Main()
    {
        var people = new[] { new { LastName = "Smith", FirstName = "Joe" },
                             new { LastName = "Sanders", FirstName = "Bob" } };

        var peopleDict = people.ToDictionary( d => d.LastName );

        // ??? foundItem <= what type would you put here?
        // peopleDict.TryGetValue( "Smith", out ??? );

        // so instead, we use our Find() extension:
        var result = peopleDict.Find( "Smith" );
        if( result.First )
        {
            Console.WriteLine( result.Second );
        }
    }
}

PS Es gibt eine andere (einfachere) Möglichkeit, die Probleme zu umgehen, die sich aus anonymen Typen in Wörterbüchern ergeben. Verwenden Sie dazu das varSchlüsselwort, damit der Compiler den Typ für Sie "ableiten" kann. Hier ist diese Version:

var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
   // use foundItem...
}
LBushkin
quelle
5
@stakx: Ja, ich stimme zu. Aber bedenken - das Beispiel sollte weitgehend Fälle zu veranschaulichen , wenn die Verwendung eines Tuple<> könnte Sinn machen. Es gibt immer Alternativen ...
LBushkin
1
Ich denke, dass das Ersetzen von Parametern durch ein Tupel wie in TryGetValue oder TryParse einer der wenigen Fälle ist, in denen es möglicherweise sinnvoll ist, wenn eine öffentliche API ein Tupel zurückgibt. Tatsächlich ändert mindestens eine .NET-Sprache diese API automatisch für Sie.
Joel Mueller
1
@stakx: Tupel sind ebenfalls unveränderlich, Arrays hingegen nicht.
Robert Paulson
2
Ich habe verstanden, dass Tupel nur als unveränderliche Pokerspielerobjekte nützlich sind.
Gennady Vanin Геннадий Ванин
2
+1 Tolle Antwort - Ich liebe deine beiden ersten Beispiele, die meine Meinung leicht geändert haben. Ich habe noch nie Beispiele gesehen, bei denen ein Tupel mindestens so gut geeignet war wie eine benutzerdefinierte Struktur oder ein benutzerdefinierter Typ. Ihr letztes "reales Beispiel" scheint mir jedoch nicht mehr viel Wert zu schaffen. Die ersten beiden Beispiele sind perfekt und einfach. Das letzte ist etwas schwer zu verstehen und Ihre "PS" macht es unbrauchbar, da Ihr alternativer Ansatz viel einfacher ist und keine Tupel erfordert.
Chiccodoro
18

Tupel können nützlich sein ... aber sie können auch später ein Schmerz sein. Wenn Sie eine Methode haben, die zurückgibt, Tuple<int,string,string,int>woher wissen Sie später, was diese Werte sind? Waren sie ID, FirstName, LastName, Ageoder waren sie UnitNumber, Street, City, ZipCode.

Matthew Whited
quelle
9

Tupel sind aus Sicht eines C # -Programmierers eine ziemlich überwältigende Ergänzung der CLR. Wenn Sie über eine Sammlung von Elementen verfügen, deren Länge variiert, müssen diese beim Kompilieren keine eindeutigen statischen Namen haben.

Wenn Sie jedoch eine Sammlung mit konstanter Länge haben, bedeutet dies, dass die festgelegten Positionen in der Sammlung jeweils eine bestimmte vordefinierte Bedeutung haben. Und es ist immer besser , ihr entsprechende statische Namen in diesem Fall zu geben, anstatt die Bedeutung zu erinnern Item1, Item2usw.

Anonyme Klassen in C # bieten bereits eine hervorragende Lösung für die häufigste private Verwendung von Tupeln, und sie geben den Elementen aussagekräftige Namen, sodass sie in diesem Sinne tatsächlich überlegen sind. Das einzige Problem ist, dass sie nicht aus benannten Methoden austreten können. Ich würde es vorziehen, wenn diese Einschränkung aufgehoben würde (möglicherweise nur für private Methoden), als eine spezifische Unterstützung für Tupel in C # zu haben:

private var GetDesserts()
{
    return _icecreams.Select(
        i => new { icecream = i, topping = new Topping(i) }
    );
}

public void Eat()
{
    foreach (var dessert in GetDesserts())
    {
        dessert.icecream.AddTopping(dessert.topping);
        dessert.Eat();
    }
}
Daniel Earwicker
quelle
Als allgemeines Merkmal würde ich gerne sehen, dass .net einen Standard für einige spezielle Arten anonymer Typen definiert, so dass z. B. struct var SimpleStruct {int x, int y;}eine Struktur mit einem Namen definiert wird, der mit einer Guid beginnt, die mit einfachen Strukturen und verknüpft ist hat so etwas wie {System.Int16 x}{System.Int16 y}angehängt; Jeder Name, der mit dieser GUID beginnt, muss eine Struktur darstellen, die nur diese Elemente und bestimmte spezifizierte Inhalte enthält. Wenn mehrere Definitionen für denselben Namen im Gültigkeitsbereich liegen und übereinstimmen, werden alle als der gleiche Typ betrachtet.
Supercat
So etwas wäre hilfreich für einige Arten von Typen, einschließlich veränderlicher und unveränderlicher Klassen- und Strukturtupel sowie Schnittstellen und Delegaten zur Verwendung in Situationen, in denen eine übereinstimmende Struktur als übereinstimmende Semantik angesehen werden sollte. Es gibt sicherlich Gründe, warum es nützlich sein kann, zwei Delegatentypen mit denselben Parametern zu haben, die nicht als austauschbar angesehen werden. Es wäre jedoch auch hilfreich, wenn man angeben könnte, dass eine Methode einen Delegaten wünscht, der eine Kombination von Parametern verwendet, darüber hinaus jedoch nicht Es ist mir egal, was für ein Delegierter es ist.
Supercat
Es ist bedauerlich, dass, wenn zwei verschiedene Personen Funktionen schreiben möchten, die als Eingabedelegierte verwendet werden, die einen einzelnen refParameter benötigen, ein Delegat nur dann an beide Funktionen übergeben werden kann, wenn eine Person a erstellt und kompiliert delegieren Sie den Typ und geben Sie ihn dem anderen zum Bauen. Es gibt keine Möglichkeit, dass beide Personen angeben können, dass die beiden Typen synonym betrachtet werden sollten.
Supercat
Haben Sie konkretere Beispiele dafür, wo Sie anonyme Klassen verwenden würden, anstatt Tuple? Ich habe gerade 50 Stellen in meiner Codebasis überflogen, an denen ich Tupel verwende, und alle sind entweder Rückgabewerte (normalerweise yield return) oder Elemente von Sammlungen, die an anderer Stelle verwendet werden. Daher sollten Sie keine anonymen Klassen verwenden.
2
@ JonofAllTrades - Die Meinungen dazu sind wahrscheinlich unterschiedlich, aber ich versuche, die öffentlichen Methoden so zu gestalten, als würden sie von jemand anderem verwendet, auch wenn ich jemand bin, also gebe ich mir einen guten Kundenservice! Sie neigen dazu, eine Funktion öfter aufzurufen, als Sie sie schreiben. :)
Daniel Earwicker
8

Ähnlich wie das Schlüsselwort varist es als Annehmlichkeit gedacht - wird aber ebenso leicht missbraucht.

Meiner bescheidensten Meinung nach nicht Tupleals Rückkehrklasse aussetzen . Verwenden Sie es privat, wenn die Datenstruktur eines Dienstes oder einer Komponente dies erfordert, geben Sie jedoch wohlgeformte bekannte Klassen aus öffentlichen Methoden zurück.

// one possible use of tuple within a private context. would never
// return an opaque non-descript instance as a result, but useful
// when scope is known [ie private] and implementation intimacy is
// expected
public class WorkflowHost
{
    // a map of uri's to a workflow service definition 
    // and workflow service instance. By convention, first
    // element of tuple is definition, second element is
    // instance
    private Dictionary<Uri, Tuple<WorkflowService, WorkflowServiceHost>> _map = 
        new Dictionary<Uri, Tuple<WorkflowService, WorkflowServiceHost>> ();
}
Johnny g
quelle
2
Die gängigen "RAD" -Sprachen (Java ist das beste Beispiel) haben sich immer für Sicherheit entschieden und vermeiden, sich in Fußszenarien selbst zu erschießen. Ich bin froh, dass Microsoft mit C # ein gewisses Risiko eingeht und der Sprache eine nette Kraft verleiht, auch wenn sie missbraucht werden kann.
Matt Greer
4
Das Schlüsselwort var kann nicht missbraucht werden. Vielleicht meintest du dynamisch?
Chris Marisic
@ Chris Marisic, nur um zu verdeutlichen, dass ich nicht im selben Szenario gemeint habe - Sie sind absolut korrekt, varkönnen nicht zurückgegeben werden oder existieren außerhalb des deklarierten Bereichs. Es ist jedoch immer noch möglich, es über den beabsichtigten Zweck hinaus zu verwenden und "missbraucht" zu werden
Johnny G
5
Ich stehe immer noch auf dieser var kann nicht missbraucht werden. Es ist lediglich ein Schlüsselwort, um eine kürzere Variablendefinition vorzunehmen. Ihr Argument handelt vielleicht von anonymen Typen, aber selbst diese können wirklich nicht missbraucht werden, da es sich immer noch um normale statisch verknüpfte Typen handelt, die jetzt dynamisch sind.
Chris Marisic
4
var kann in dem Sinne missbraucht werden, dass es bei Überbeanspruchung manchmal Probleme für die Person verursachen kann, die den Code liest. Vor allem, wenn Sie nicht in Visual Studio sind und keine Intelligenz haben.
Matt Greer
5

Die Verwendung einer Klasse wie ResultTypeist klarer. Sie können den Feldern in der Klasse aussagekräftige Namen geben (mit einem Tupel würden sie Item1und genannt Item2). Dies ist umso wichtiger, wenn die Typen der beiden Felder gleich sind: Der Name unterscheidet sie klar voneinander.

Richard Fearn
quelle
Ein kleiner zusätzlicher Vorteil: Sie können die Parameter für eine Klasse sicher neu anordnen. Wenn Sie dies für ein Tupel tun, wird der Code beschädigt. Wenn die Felder ähnliche Typen haben, kann dies beim Kompilieren möglicherweise nicht erkannt werden.
Ich stimme zu, Tupel werden verwendet, wenn einem Entwickler die Energie fehlt, an einen aussagekräftigen Klassennamen zu denken, der aussagekräftige Eigenschaftsnamen gruppiert. Beim Programmieren geht es um Kommunikation. Tupel sind ein Gegenmuster zur Kommunikation. Ich hätte es vorgezogen, wenn Microsoft die Sprache weggelassen hätte, um Entwickler zu zwingen, gute Designentscheidungen zu treffen.
Mike Gold
5

Wie wäre es mit Tupeln in einem Muster zum Dekorieren, Sortieren und Nichtdekorieren? (Schwartzianische Transformation für die Perl). Hier ist zwar ein erfundenes Beispiel, aber Tupel scheinen ein guter Weg zu sein, um mit solchen Dingen umzugehen:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] files = Directory.GetFiles("C:\\Windows")
                    .Select(x => new Tuple<string, string>(x, FirstLine(x)))
                    .OrderBy(x => x.Item2)
                    .Select(x => x.Item1).ToArray();
        }
        static string FirstLine(string path)
        {
            using (TextReader tr = new StreamReader(
                        File.Open(path, FileMode.Open)))
            {
                return tr.ReadLine();
            }
        }
    }
}

Jetzt hätte ich ein Objekt [] aus zwei Elementen oder in diesem speziellen Beispiel eine Zeichenfolge [] aus zwei Elementen verwenden können. Der Punkt ist, dass ich alles als zweites Element in einem Tupel hätte verwenden können, das intern verwendet wird und ziemlich einfach zu lesen ist.

Clinton Pierce
quelle
3
Sie können Tuple.Create anstelle des Konstruktors verwenden. Es ist kürzer.
Kugel
Ein anonymer Typ ist viel besser lesbar. als würde man echte Variablennamen anstelle von 'x' verwenden
Dave Cousineau
Es würde jetzt . Das ist ein SO-Problem. Antworten, die vor Jahren veröffentlicht wurden, werden nie für neue Technologien bereinigt oder aktualisiert.
Clinton Pierce
4

IMO sind diese "Tupel" im Grunde alle anonymen structTypen mit öffentlichem Zugriff und unbenannten Mitgliedern .

Der einzige Ort, an dem ich Tupel verwenden würde, ist, wenn Sie einige Daten in einem sehr begrenzten Umfang schnell zusammenfügen müssen. Die Semantik der Daten sollte offensichtlich sein , damit der Code nicht schwer zu lesen ist. Die Verwendung eines Tupels ( int, int) für (row, col) erscheint daher sinnvoll. Es fällt mir jedoch schwer, einen Vorteil gegenüber einem structmit namentlich genannten Mitgliedern zu finden (es werden also keine Fehler gemacht und Zeilen / Spalten werden nicht versehentlich ausgetauscht).

Wenn Sie Daten an den Anrufer zurücksenden oder Daten von einem Anrufer akzeptieren, sollten Sie a structmit benannten Mitgliedern verwenden.

Nehmen Sie ein einfaches Beispiel:

struct Color{ float r,g,b,a ; }
public void setColor( Color color )
{
}

Die Tupelversion

public void setColor( Tuple<float,float,float,float> color )
{
  // why?
}

Ich sehe keinen Vorteil darin, Tupel anstelle einer Struktur mit benannten Mitgliedern zu verwenden. Die Verwendung unbenannter Mitglieder ist ein Rückschritt für die Lesbarkeit und Verständlichkeit Ihres Codes.

Tupel scheint mir ein fauler Weg zu sein, um zu vermeiden, ein structmit tatsächlich benannten Mitgliedern zu erstellen . Übermäßiger Gebrauch von Tupel, bei dem Sie wirklich das Gefühl haben, dass Sie / oder jemand anderes, der auf Ihren Code stößt, benannte Mitglieder benötigt, ist A Bad Thing ™, falls ich jemals eines gesehen habe.

Bobobobo
quelle
3

Beurteilen Sie mich nicht, ich bin kein Experte, aber mit neuen Tupeln in C # 7.x könnten Sie etwas zurückgeben wie:

return (string Name1, int Name2)

Zumindest können Sie es jetzt benennen und Entwickler sehen möglicherweise einige Informationen.

Denis Gordin
quelle
2

Das kommt natürlich darauf an! Wie Sie sagten, kann ein Tupel Ihnen Code und Zeit sparen, wenn Sie einige Artikel für den lokalen Verbrauch zusammenfassen möchten. Sie können sie auch verwenden, um allgemeinere Verarbeitungsalgorithmen zu erstellen, als wenn Sie eine konkrete Klasse weitergeben. Ich kann mich nicht erinnern, wie oft ich mir gewünscht habe, etwas anderes als KeyValuePair oder eine DataRow zu haben, um schnell ein Datum von einer Methode zur anderen zu übergeben.

Auf der anderen Seite ist es durchaus möglich, es zu übertreiben und Tupel herumzugeben, bei denen man nur raten kann, was sie enthalten. Wenn Sie ein Tupel für mehrere Klassen verwenden möchten, ist es möglicherweise besser, eine konkrete Klasse zu erstellen.

In Maßen verwendet, können Tupel natürlich zu einem präziseren und lesbareren Code führen. In C ++, STL und Boost finden Sie Beispiele für die Verwendung von Tupeln in anderen Sprachen. Am Ende müssen wir jedoch alle experimentieren, um herauszufinden, wie sie am besten in die .NET-Umgebung passen.

Panagiotis Kanavos
quelle
1

Tupel sind eine nutzlose Framework-Funktion in .NET 4. Ich denke, mit C # 4.0 wurde eine großartige Gelegenheit verpasst. Ich hätte gerne Tupel mit benannten Mitgliedern gehabt, damit Sie auf die verschiedenen Felder eines Tupels mit Namen anstelle von Wert1 , Wert2 usw. zugreifen können.

Es hätte eine Änderung der Sprache (Syntax) erforderlich gemacht, aber es wäre sehr nützlich gewesen.

Philippe Leybaert
quelle
Wie sind Tupel ein "Sprachmerkmal"? Es ist eine Klasse, die für alle .net-Sprachen verfügbar ist, nicht wahr?
Jason Webb
11
Vielleicht relativ nutzlos für C #. Aber System.Tuple wurde wegen C # nicht zum Framework hinzugefügt. Es wurde hinzugefügt, um die Interoperabilität zwischen F # und anderen Sprachen zu vereinfachen.
Joel Mueller
@ Jason: Ich habe nicht gesagt, dass es sich um eine Sprachfunktion handelt (ich habe das falsch geschrieben, aber ich habe es korrigiert, bevor Sie diesen Kommentar abgegeben haben).
Philippe Leybaert
2
@Jason - Sprachen mit direkter Unterstützung für Tupel unterstützen implizit die Dekonstruktion von Tupeln in ihre Komponentenwerte. In diesen Sprachen geschriebener Code greift normalerweise niemals auf die Eigenschaften Item1, Item2 zu. let success, value = MethodThatReturnsATuple()
Joel Mueller
@ Joel: Diese Frage ist mit C # gekennzeichnet, es geht also irgendwie um C #. Ich denke immer noch, dass sie es zu einem erstklassigen Feature in der Sprache hätten machen können.
Philippe Leybaert
1

Ich persönlich würde niemals ein Tupel als Rückgabetyp verwenden, da es keinen Hinweis darauf gibt, was die Werte darstellen. Tupel haben einige wertvolle Verwendungszwecke, da sie im Gegensatz zu Objekten Werttypen sind und somit Gleichheit verstehen. Aus diesem Grund werde ich sie als Wörterbuchschlüssel verwenden, wenn ich einen mehrteiligen Schlüssel benötige, oder als Schlüssel für eine GroupBy-Klausel, wenn ich nach mehreren Variablen gruppieren möchte und keine verschachtelten Gruppierungen möchte (Wer möchte jemals verschachtelte Gruppierungen?). Um das Problem mit extremer Ausführlichkeit zu lösen, können Sie sie mit einer Hilfsmethode erstellen. Denken Sie daran, wenn Sie häufig auf Mitglieder zugreifen (über Item1, Item2 usw.), sollten Sie wahrscheinlich ein anderes Konstrukt verwenden, z. B. eine Struktur oder eine anonyme Klasse.

Seth Paulson
quelle
0

Ich habe sowohl Tupel Tupleals auch neue Tupel ValueTuplein verschiedenen Szenarien verwendet und bin zu folgendem Schluss gekommen: Nicht verwenden .

Jedes Mal sind folgende Probleme aufgetreten:

  • Code wurde aufgrund fehlender starker Benennung unlesbar;
  • Klassenhierarchiefunktionen wie DTOs der Basisklasse und DTOs der untergeordneten Klasse usw. können nicht verwendet werden.
  • Wenn sie an mehreren Stellen verwendet werden, werden diese hässlichen Definitionen anstelle eines sauberen Klassennamens kopiert und eingefügt.

Meiner Meinung nach sind Tupel ein Nachteil und kein Merkmal von C #.

Ich habe etwas ähnliche, aber viel weniger harte Kritik an Func<>und Action<>. Diese sind in vielen Situationen nützlich, insbesondere in einfachen Actionund Func<type>Varianten, aber darüber hinaus habe ich festgestellt, dass das Erstellen eines Delegatentyps eleganter, lesbarer und wartbarer ist und Ihnen mehr Funktionen wie ref/ outparameter bietet .

Herr TA
quelle
Ich erwähnte benannte Tupel - ValueTuple- und ich mag sie auch nicht. Erstens ist die Benennung nicht stark, sondern eher ein Vorschlag, der sehr leicht durcheinander zu bringen ist, da sie implizit entweder Tupleoder ValueTuplemit derselben Anzahl und denselben Arten von Elementen zugewiesen werden kann. Zweitens sind der Mangel an Vererbung und die Notwendigkeit, die gesamte Definition von Ort zu Ort zu kopieren und einzufügen, immer noch nachteilig.
Herr TA
Ich stimme diesen Punkten zu. Ich versuche, Tupel nur zu verwenden, wenn ich den Produzenten und den Konsumenten kontrolliere und wenn sie nicht zu "weit weg" sind. Das heißt, Produzent und Konsument in derselben Methode = "nah", während Produzent und Konsument in verschiedenen Baugruppen = "Lichtjahre entfernt". Also, wenn ich Tupel am Anfang einer Methode erstelle und sie am Ende verwende? Klar, damit bin ich einverstanden. Ich dokumentiere auch die Bedingungen und so. Aber ja, ich stimme zu, dass sie im Allgemeinen nicht die richtige Wahl sind.
Mike Christiansen