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 out
Parameter 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 var
Schlü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...
}