À mon avis, un Tuple est un raccourci pour écrire une classe de résultat (je suis sûr qu'il existe également d'autres utilisations).
Il existe en effet d'autres utilisations intéressantesTuple<>
- la plupart d'entre elles impliquent d'abstraire la sémantique d'un groupe particulier de types partageant une structure similaire et de les traiter simplement comme un ensemble ordonné de valeurs. Dans tous les cas, l'un des avantages des tuples est qu'ils évitent d'encombrer votre espace de noms avec des classes de données uniquement qui exposent des propriétés mais pas des méthodes.
Voici un exemple d'utilisation raisonnable pour Tuple<>
:
var opponents = new Tuple<Player,Player>( playerBob, playerSam );
Dans l'exemple ci-dessus, nous voulons représenter une paire d'adversaires, un tuple est un moyen pratique de coupler ces instances sans avoir à créer une nouvelle classe. Voici un autre exemple:
var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );
Une main de poker peut être considérée comme un simple jeu de cartes - et le tuple (peut être) une manière raisonnable d'exprimer ce concept.
en mettant de côté la possibilité que je manque le point des tuples, est-ce que l'exemple avec un Tuple est un mauvais choix de conception?
Le renvoi d' Tuple<>
instances fortement typées dans le cadre d'une API publique pour un type public est rarement une bonne idée. Comme vous le reconnaissez vous-même, les tuples exigent que les parties impliquées (auteur de la bibliothèque, utilisateur de la bibliothèque) se mettent d'accord à l'avance sur le but et l'interprétation des types de tuple utilisés. Il est déjà assez difficile de créer des API intuitives et claires, l'utilisation Tuple<>
publique obscurcit uniquement l'intention et le comportement de l'API.
Les types anonymes sont également une sorte de tuple - cependant, ils sont fortement typés et vous permettent de spécifier des noms clairs et informatifs pour les propriétés appartenant au type. Mais les types anonymes sont difficiles à utiliser dans différentes méthodes - ils ont été principalement ajoutés pour prendre en charge des technologies telles que LINQ où les projections produiraient des types auxquels nous ne voudrions normalement pas attribuer des noms. (Oui, je sais que les types anonymes avec les mêmes types et propriétés nommées sont consolidés par le compilateur).
Ma règle de base est la suivante: si vous le renvoyez depuis votre interface publique, faites-en un type nommé .
Mon autre règle de base pour l'utilisation des tuples est la suivante: nommez les arguments de méthode et les variables localc de type Tuple<>
aussi clairement que possible - faites en sorte que le nom représente la signification des relations entre les éléments du tuple. Pensez à mon var opponents = ...
exemple.
Voici un exemple de cas réel où j'ai utilisé Tuple<>
pour éviter de déclarer un type de données uniquement à utiliser uniquement dans mon propre assembly . La situation implique le fait que lors de l'utilisation de dictionnaires génériques contenant des types anonymes, il devient difficile d'utiliser la TryGetValue()
méthode pour trouver des éléments dans le dictionnaire car la méthode nécessite un out
paramètre qui ne peut pas être nommé:
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 Il existe un autre moyen (plus simple) de contourner les problèmes liés aux types anonymes dans les dictionnaires, et c'est d'utiliser le var
mot - clé pour laisser le compilateur «déduire» le type pour vous. Voici cette version:
var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
// use foundItem...
}