Quelle exigence le tuple a-t-il été conçu pour résoudre?

94

Je regarde la nouvelle fonctionnalité C # des tuples. Je suis curieux de savoir quel problème le tuple a-t-il été conçu pour résoudre?

Pourquoi avez-vous utilisé des tuples dans vos applications?

Mettre à jour

Merci pour les réponses jusqu'à présent, laissez-moi voir si j'ai des choses claires dans mon esprit. Un bon exemple de tuple a été indiqué comme coordonnées. Cela semble-t-il correct?

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

Ensuite, utilisez le tuple comme ceci:

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

Est-ce exact?

Chaddeus
la source
4
Eh bien, je peux penser à deux réponses ...
Noon Silk
13
Si le concept de «coordonnée» a un sens en tant que classe, je le ferais en une classe. Utilisez des tuples pour les situations où il n'y a pas de concept de «logique métier» raisonnable pour l'ensemble de données qui justifie la création d'un type.
Eric Lippert
13
Les coordonnées ne sont pas un bon exemple de tuple. Tuple en C # est juste une solution ad hoc dans les cas où vous devez renvoyer (à partir d'une fonction) deux valeurs. Au lieu de définir un type de résultat plus un paramètre de sortie, il est plus élégant de renvoyer un tuple. De cette façon, vous n'avez pas à déclarer le deuxième paramètre à l'avance. La différence est encore plus claire en C ++ (vous pouvez définir le résultat, mais vous ne pouvez pas définir le paramètre out / ref).
greenoldman
8
Parce que python l'avait, et python ne peut rien avoir que C # n'a pas;)
Evan Plaice

Réponses:

117

Lors de l'écriture de programmes, il est extrêmement courant de vouloir regrouper logiquement un ensemble de valeurs qui n'ont pas suffisamment de points communs pour justifier la création d'une classe.

De nombreux langages de programmation vous permettent de regrouper logiquement un ensemble de valeurs autrement sans rapport sans créer de type d'une seule manière:

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

Logiquement, c'est exactement la même chose qu'une méthode M qui prend un argument qui est un 3-tuple de int, string, double. Mais j'espère que vous ne feriez pas réellement:

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

à moins que MArguments ait une autre signification dans la logique métier.

Le concept de «regrouper un tas de données autrement sans rapport dans une structure plus légère qu'une classe» est utile dans de très nombreux endroits, pas seulement pour les listes de paramètres formelles de méthodes. C'est utile lorsqu'une méthode a deux choses à renvoyer, ou lorsque vous souhaitez saisir un dictionnaire de deux données plutôt qu'une, et ainsi de suite.

Les langages comme F # qui prennent en charge les types de tuple offrent nativement une grande flexibilité à leurs utilisateurs; ils constituent un ensemble extrêmement utile de types de données. L'équipe BCL a décidé de travailler avec l'équipe F # pour standardiser sur un type de tuple pour le framework afin que chaque langage puisse en bénéficier.

Cependant, il n'existe à ce stade aucune prise en charge du langage pour les tuples en C #. Les tuples ne sont qu'un autre type de données comme toute autre classe de framework; il n'y a rien de spécial à leur sujet. Nous envisageons d'ajouter un meilleur support pour les tuples dans les futures versions hypothétiques de C #. Si quelqu'un a des idées sur le type de fonctionnalités impliquant des tuples que vous aimeriez voir, je serais heureux de les transmettre à l'équipe de conception. Les scénarios réalistes sont plus convaincants que les réflexions théoriques.

Eric Lippert
la source
1
@MalcomTucker: L'asychronie et le parallélisme sont définitivement dans nos esprits en tant que domaines riches où les outils linguistiques pourraient être utilisés pour résoudre de vrais problèmes. Je ne pense pas que les workflows asynchrones de style F # soient nécessairement les meilleurs pour C #, mais ils sont définitivement inspirants.
Eric Lippert
60
Bien que les tuples soient utiles, un gros inconvénient est la clarté. Il est difficile de lire et de comprendre le code qui fait référence à Item1, Item2etc ... Si tuples jamais faire obtenir un soutien linguistique en C #, il serait merveilleux de permettre à leurs membres d'être nommés (ou au moins aliasées) de manière à permettre le code qui utilise pour être plus compréhensibles. Dans un futur si profond, j'aimerais aussi voir des tuples de «forme» appropriée comme des paramètres légaux aux méthodes qui prennent des paramètres individuels (et vice versa). Donc, Tuple<int,string,bool>peut être transmis à M(int,string,bool). Cela rendrait les méthodes de mémorisation beaucoup plus faciles.
LBushkin
2
Ces "tuples" sont essentiellement tous des types d' publicaccès, anonymes struct avec des membres sans nom . Je préférerais que le programmeur écrive un structavec des membres nommés et le renvoie, plutôt que de devoir traiter avec un tuplequi ne me dit rien sur la sémantique de ses membres.
bobobobo
1
@ Hi-Angel: L'argument ne consiste pas à ergoter sur ce qui compte comme un tuple et ce qui ne l'est pas, mais plutôt sur l'ensemble des fonctionnalités que les développeurs de secteurs d'activité modernes pourraient utiliser pour augmenter leur productivité . LBushkin exprime une demande de fonctionnalité que l'équipe de conception entend tout le temps: le désir de créer un "type d'enregistrement" quelconque sans la peine et les frais de créer une classe entière juste pour contenir quelques champs. C # a déjà cette fonctionnalité pour les méthodes; cela s'appelle la "liste de paramètres".
Eric Lippert
2
@ Salut-Angel: Vous ne feriez probablement pas l'argument "si vous voulez accéder aux paramètres de votre méthode par le nom, vous devriez créer une classe séparée" parce que cela semble extrêmement lourd, mais cela ne semble lourd que parce que nous avons l'habitude d'avoir le possibilité de lier instantanément un ensemble de variables arbitraires avec des types et des noms arbitraires lors de l'appel d'une méthode . Imaginez qu'aucune langue n'ait cette caractéristique; chaque méthode ne peut prendre qu'un seul argument, et si vous voulez en passer deux, vous devez créer un type et en transmettre une instance.
Eric Lippert
22

Les tuples fournissent une implémentation immuable d'une collection

Outre les utilisations courantes des tuples:

  • pour regrouper des valeurs communes sans avoir à créer de classe
  • pour renvoyer plusieurs valeurs d'une fonction / méthode
  • etc...

Les objets immuables sont intrinsèquement thread-safe:

Les objets immuables peuvent être utiles dans les applications multithreads. Plusieurs threads peuvent agir sur des données représentées par des objets immuables sans se soucier des données modifiées par d'autres threads. Les objets immuables sont donc considérés comme plus sûrs pour les threads que les objets mutables.

Depuis "Objet immuable" sur wikipedia

Plie d'Evan
la source
Merci d'avoir ajouté des informations supplémentaires à la question. Très appréciée.
Chaddeus
Si vous voulez une collection immuable, vous devez utiliser une collection immuable, pas un Tuple. La différence est que pour la plupart des collections immuables, vous n'avez pas à spécifier le nombre d'éléments à la compilation
BlueRaja - Danny Pflughoeft
@ BlueRaja-DannyPflughoeft À l'époque où j'ai écrit cette réponse, les classes de collection immuables C # n'étaient toujours pas disponibles dans la BCL. Je suppose que je changerai 'collection' en 'objet' depuis que MS a stérilisé l'implémentation Tuple en C #
Evan Plaice
12

Il fournit une alternative à refou outsi vous avez une méthode qui doit renvoyer plusieurs nouveaux objets dans le cadre de sa réponse.

Il vous permet également d'utiliser un type intégré comme type de retour si tout ce que vous avez à faire est de combiner deux ou trois types existants et que vous ne voulez pas avoir à ajouter une classe / structure juste pour cette combinaison. (Avez-vous déjà souhaité qu'une fonction renvoie un type anonyme? C'est une réponse partielle à cette situation.)

Toby
la source
Zut. Agréable. J'aurais aimé vérifier ce que sont les tuples il y a quelques années;).
sabiland
10

Il est souvent utile d'avoir un type "paire", juste utilisé dans des situations rapides (comme retourner deux valeurs d'une méthode). Les tuples sont une partie centrale des langages fonctionnels comme F #, et C # les a repris en cours de route.

Stephen Cleary
la source
System.Web.UI a une classe Pair msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx .
StingyJack
5

très utile pour renvoyer deux valeurs à partir d'une fonction

Keith Nicholas
la source
1
Peut-être pour envelopper un type anonyme?
bloparod
1
Je les vois en quelque sorte comme des structures anonymes
Matt Evans
4

Personnellement, je trouve que Tuples est une partie itérative du développement lorsque vous êtes dans un cycle d'investigation ou que vous "jouez". Parce qu'un Tuple est générique, j'ai tendance à y penser lorsque je travaille avec des paramètres génériques - en particulier lorsque je veux développer un morceau de code générique, et je commence par la fin du code, au lieu de me demander "comment j'aimerais cet appel regarder?".

Très souvent, je me rends compte que la collection que les formes Tuple font partie d'une liste, et regarder List> n'exprime pas vraiment l'intention de la liste, ni comment elle fonctionne. Je "vis" souvent avec, mais j'ai envie de manipuler la liste et de changer une valeur - à quel point, je ne veux pas nécessairement créer un nouveau Tuple pour cela, donc je dois créer ma propre classe ou structure pour le tenir, donc je peux ajouter du code de manipulation.

Bien sûr, il existe toujours des méthodes d'extension - mais bien souvent, vous ne voulez pas étendre ce code supplémentaire à des implémentations génériques.

Il y a eu des moments où je voulais exprimer des données sous forme de tuple, et je n'avais pas de tuples disponible. (VS2008) auquel cas je viens de créer ma propre classe Tuple - et je ne la rend pas thread-safe (immuable).

Je suppose donc que je suis d'avis que les tuples sont une programmation paresseuse au détriment de la perte d'un nom de type qui décrit son but. L'autre dépense est que vous devez déclarer la signature du Tuple partout où elle est utilisée comme paramètre. Après un certain nombre de méthodes qui commencent à paraître gonflées, vous pouvez ressentir comme moi qu'il vaut la peine de créer une classe, car elle nettoie les signatures de méthode.

J'ai tendance à commencer par avoir la classe en tant que membre public de la classe dans laquelle vous travaillez déjà. Mais au moment où elle s'étend au-delà d'une simple collection de valeurs, elle obtient son propre fichier, et je la déplace hors de la classe contenant.

Donc, rétrospectivement, je crois que j'utilise Tuples quand je ne veux pas aller écrire un cours, et que je veux juste penser à ce que j'écris en ce moment. Ce qui signifie que la signature du Tuple peut changer beaucoup dans le texte une demi-heure pendant que je détermine les données dont je vais avoir besoin pour cette méthode et comment elle renvoie les valeurs qu'elle renverra.

Si j'ai la chance de refactoriser le code, je remets souvent en question la place d'un Tuple dans celui-ci.

Simon Miller
la source
3

Ancienne question depuis 2010, et maintenant en 2017, Dotnet change et devient plus intelligent.

C # 7 introduit la prise en charge du langage pour les tuples, qui active les noms sémantiques pour les champs d'un tuple à l'aide de nouveaux types de tuple plus efficaces.

Dans vs 2017 et .Net 4.7 (ou en installant le package nuget System.ValueTuple), vous pouvez créer / utiliser un tuple de manière très efficace et simple:

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

Renvoyer plusieurs valeurs d'une méthode:

    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}");    

Pour plus de détails, lisez: https://docs.microsoft.com/en-us/dotnet/csharp/tuples

M. Hassan
la source
1

Un tuple est souvent utilisé pour renvoyer plusieurs valeurs à partir de fonctions lorsque vous ne souhaitez pas créer un type spécifique. Si vous êtes familier avec Python, Python a cela depuis longtemps.

Randy Minder
la source
1

Renvoyer plus d'une valeur à partir d'une fonction. getCoordinates () n'est pas très utile s'il renvoie simplement x, y ou z, mais créer une classe et un objet complets pour contenir trois entiers semble également assez lourd.

Dean J
la source
1

Une utilisation courante peut être d'éviter de créer des classes / structures qui ne contiennent que 2 champs, à la place vous créez un Tuple (ou un KeyValuePair pour le moment). Utile comme valeur de retour, évitez de passer N paramètres ...

user347594
la source
0

Je trouve que KeyValuePair est actualisé en C # pour parcourir les paires clé / valeur dans un dictionnaire.

Jubal
la source
KeyValuePairpeut être considéré comme une solution de contournement spéciale. En Python, l'itération sur un dictsimple retourne des tuples (clé, valeur).
dan04
Oui, comme python. :-) Je ne savais pas que KeyValuePair en c # n'était pas un tuple?
Jubal
0

C'est vraiment utile pour renvoyer les valeurs des fonctions. Nous pouvons avoir plusieurs valeurs en retour et c'est tout à fait un économiseur dans certains scénarios.

Akshat
la source
0

Je suis tombé sur ce benchmark de performance entre les paires tuples et clé-valeur et vous le trouverez probablement intéressant. En résumé, il dit que Tuple a l'avantage parce que c'est une classe, donc il est stocké dans le tas et non dans la pile et lorsqu'il est passé en argument, son pointeur est la seule chose qui va. Mais KeyValuePair est une structure, donc il est plus rapide à allouer mais il est plus lent lorsqu'il est utilisé.

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

Ognyan Dimitrov
la source