web-dev-qa-db-fra.com

Quand utiliser: Tuple vs Class c # 7.0

Avant Tuples, j'avais l'habitude de créer une class et ses variables, puis de créer un objet à partir de cette classe et de faire de cet objet le type de retour pour certaines fonctions. 

Maintenant, avec les tuples, je peux faire la même chose et en c # 7.0, nous pouvons assigner des noms compréhensibles pour les propriétés des tuples (auparavant, c'était item1, item2, etc.) 

Alors maintenant je me demande, quand devrais-je utiliser des n-uplets et quand devrais-je créer une classe en c # 7.0?

47
Madonna Remon

Comme cette réponse est source de confusion chez certaines personnes ici, je devrais préciser que - conformément à la question - toutes les références à "Tuple" font ici référence au type ValueTuple et aux nouvelles fonctionnalités sucre Tuple de C # 7 et anciens types de référence System.Tuple.

Alors maintenant, je me demande quand devrais-je utiliser des n-uplets et quand devrais-je créer une classe en c # 7.0?

Vous seul pouvez vraiment répondre à cette question, car cela dépend vraiment de votre code. 

Cependant, il existe des directives et des règles que vous pouvez suivre pour vous aider à choisir entre elles:

Les tuples sont des valeurs, elles sont donc copiées par valeur plutôt que par référence.

La plupart du temps, cela ne devrait pas être un problème. Cependant, si vous passez des n-uplets de grandes structures, cela peut avoir un impact sur les performances. Les sections locales/retours peuvent être utilisées pour contourner ces problèmes de performances.

De plus, comme il s'agit de valeurs, la modification d'une copie à distance ne modifiera pas la copie d'origine. Ceci est une bonne chose, mais pourrait attraper certaines personnes.

Les noms d'éléments du tuple ne sont pas conservés

Les noms donnés aux éléments sont utilisés par le compilateur et (dans la plupart des cas) ne sont pas disponibles au moment de l'exécution. Cela signifie que la réflexion ne peut pas être utilisée pour découvrir leurs noms; ils ne sont pas accessibles de manière dynamique et ne peuvent pas être utilisés dans des vues rasantes.

C'est également un élément important à prendre en compte avec les API. Un tuple renvoyé par une méthode est l'exception à la règle concernant la possibilité de découvrir le nom après la compilation. Le compilateur ajoute des attributs à la méthode contenant des informations sur les noms de tuple. Cela signifie que vous pouvez retourner en toute sécurité un tuple d'une méthode publique dans un assembly et accéder à ses noms dans un autre.

Les tuples sont légers

Les nuplets sont beaucoup plus simples à écrire que les types car ils sont moins verbeux et la déclaration peut être "en ligne" (c'est-à-dire déclarée au moment de l'utilisation). Cela fonctionne bien lors de la déclaration d'une méthode qui renvoie plusieurs valeurs, par exemple.

Cependant, comme ils sont déclarés au moment de l’utilisation, si vous avez MethodA qui appelle MethodB qui appelle MethodC et que chacun renvoie un tuple, vous devrez redéfinir le tuple à chaque étape. Il n'y a pas ( encore ) de moyen de créer un alias de Tuple et de le réutiliser via plusieurs méthodes.

Il suffit d'utiliser le bon sens

Pour toute situation dans laquelle vous pourriez envisager d'utiliser un tuple: posez-vous simplement la question: "un tuple simplifiera-t-il le code ici". Si la réponse est "oui", utilisez-en un. Et c’est finalement la considération primordiale pour savoir s’il faut utiliser un tuple ou une classe personnalisée.

29
David Arno

De manière générale, les classes nommées ont une certaine importance dans la conception de votre système. Ils sont aussi plus verbeux à écrire. Par exemple, vous pouvez avoir une classe appelée MediaFileOpener. Il est important pour la conception que nous sachions ce que fait cette classe - nous travaillons avec des fichiers multimédias!

Les types et les nuplets anonymes sont utilisés lorsqu'il n'y a aucune importance conceptuelle et que vous souhaitez simplement un objet de transfert de données (DTO) léger pour déplacer les informations.

En règle générale, si votre classe a besoin de documentation pour décrire à quoi elle sert, ou si elle comporte un comportement, utilisez une classe complète. Si tout ce dont vous avez besoin est un stockage temporaire ou une sorte de regroupement, utilisez un tuple. Envisagez une situation dans laquelle vous souhaitez renvoyer plusieurs valeurs d'une méthode asynchrone. Tuple est conçu pour résoudre ce problème.

18
Gusdor

Utiliser une classe

Si vos objets sont des entités largement utilisées dans votre application et sont également stockées dans un stockage persistant tel qu'une base de données relationnelle (SQL Server, MySQL, SQLite), une base de données NoSQL ou un cache fichiers texte ou CSV.

Alors oui, tout ce qui est persistant devrait avoir sa propre classe.

Utilisez un tuple

Si vos objets ont une courte durée de vie sans signification particulière pour votre application. Par exemple, si vous devez renvoyer rapidement une paire de coordonnées, il est préférable d’avoir quelque chose comme ceci:

(double Latitude, double Longitude) getCoordinates()
{
    return (144.93525, -98.356346);
}

que définir une classe séparée 

class Coordinates
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

Un tuple vous évitera d'avoir à allouer de la mémoire sur le tas en utilisant new pour une opération aussi simple.

Une autre fois où je trouve les nuplets utiles, c’est lorsqu’on effectue plusieurs opérations mathématiques sur des opérandes

(double Result1, double Result2, double Result3) performCalculations(int operand1, int operand 2)

Cela n'a aucun sens de définir une classe dans ce cas. Quels que soient les calculs, les résultats n'appartiennent pas à une classe. Donc, l’alternative serait d’utiliser des variables out mais je pense que les n-uplets sont plus expressifs et qu’ils améliorent la lisibilité.

7
dimlucas

Je veux commencer par mentionner que C # prenait déjà en charge les types anonymous . Quels sont les types de référence. Ainsi, vous aviez déjà une alternative appropriée à la création d'une classe nommée.

L'un des avantages d'une classe nommée est qu'il sera plus facile de le réutiliser (par exemple, si vous avez besoin du même type à plusieurs endroits) et de documenter. Étant donné que le type anonyme est anonyme, vous ne pouvez obtenir les variables typées que si vous pouvez utiliser var, ce qui limite les contextes dans lesquels les types anonymes sont utiles (par exemple, vous ne pouvez pas les utiliser comme types de champ, types de retour ou types de paramètre ).

Bien sûr, vous pouvez passer en revue certaines des limitations des types anonymes en utilisant System.Tuple . Qui est aussi un type de référence, et vous pouvez l’utiliser explicitement. L'inconvénient est qu'il manque des noms personnalisés pour les membres.


Les tuples C # 7 ( ValueTuple ) peuvent être considérés comme similaires aux types anonymes. La première différence est qu'ils sont des types de valeur. Cela signifie que ces n-uplets auront un avantage en termes de performances tant qu'ils resteront dans la portée locale ou se déplaceront dans la pile (utilisation courante des types anonymes, en raison de sa limitation).

La deuxième différence est que la nouvelle syntaxe permet aux tuples d’apparaître à plus d’endroits que de types anonymes, comme vous le savez, vous avez le sucre syntaxique de définir le type de retour à ValueTuple (lors de l’utilisation de types anonymes, vous deviez retourner object). 

La troisième différence est que ValueTuple prend en charge la déconstruction en sortie de boîte. Pour citer Quoi de neuf en C # 7.0 :

Une autre façon de consommer des n-uplets est de les déconstruire. Une déclaration de déconstruction est une syntaxe permettant de scinder un tuple (ou une autre valeur) en ses parties et d'assigner ces parties individuellement à de nouvelles variables:

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");

Ce que vous pouvez également faire avec des types personnalisés en ajoutant une méthode Deconstruct .


Pour résumé:

  • ValueTuple a un sucre syntaxique en C # 7.0, ce qui devrait être pris en compte pour la lisibilité.
  • ValueTuple est un type de valeur. Tous les avantages et les inconvénients entre l’utilisation de class et de struct s’appliquent.
  • ValueTuple peut être utilisé explicitement (avec ou sans le sucre syntaxique), ce qui lui permet d’avoir la polyvalence de System.Tuple tout en conservant les membres nommés.
  • ValueTuple prend en charge la déconstruction.

Étant donné que le sucre syntaxique est incontournable, je dirais que l'argument le plus fort pour choisir ValueTuple est le même argument que pour choisir un struct. Ce qui serait idéal pour les petits types immuables, qui vivent principalement sur la pile (vous n'avez donc pas beaucoup de boxe et de déballage).

En comparant ValueTuple à une structure complète, en considérant le sucre syntaxique, je suggérerais d'utiliser ValueTuple par défaut, sauf si vous avez besoin d'une disposition explicit ou de l'ajout de méthodes.

Je tiens également à dire que le sucre syntaxique n'améliore pas nécessairement la lisibilité. La raison principale étant que vous ne nommez pas le type et que le nom du type donne un sens au code. De plus, vous pouvez ajouter de la documentation dans une déclaration struct ou class qui facilite la compréhension.

Au total, la situation où ValueTuple brille vraiment renvoie des valeurs multiples à partir d'une méthode. Dans ce cas, il n'est plus nécessaire de créer de nouveaux paramètres out. Et la documentation de la ValueTuple utilisée peut vivre dans la documentation de la méthode. Si vous pensez que vous devez faire autre chose avec la variable ValueTuple (en définissant des méthodes d'extension, par exemple), je suggérerais plutôt de créer un type nommé.

3
Theraot

Les tuples sont destinés à représenter plusieurs valeurs, comme lorsqu'une méthode a l'intention de renvoyer plusieurs valeurs. Le support Tuple dans C # 7 utilise les instances System.ValueTuple<...> pour représenter cet ensemble de valeurs. Les noms de ces valeurs ne sont valides que dans le contexte où elles sont utilisées et ne sont pas appliquées.

Les classes sont destinées à représenter une seule valeur avec plusieurs attributs.

2
Paulo Morgado

Généralement, vous voulez avoir une classe lorsque votre objet sera utilisé ailleurs ou s'il représente un objet réel ou un concept de votre domaine. Vous allez probablement créer une classe pour représenter une voiture ou un magasin de voiture, pas des n-uplets.

D'autre part, parfois, vous voulez simplement renvoyer quelques objets d'une méthode. Peut-être qu'ils ne représentent rien de spécial, c'est juste que vous devez les renvoyer ensemble avec cette méthode particulière. Parfois, même s'ils représentent un concept de votre domaine (supposons que vous retourniez (Car, Store), qui pourrait être représenté sous la forme d'un objet Sale), vous ne les utiliserez réellement nulle part, vous ne faites que déplacer des données. Dans ces cas, c'est bien d'utiliser des n-uplets.

Maintenant, en parlant de C #, il y a une dernière chose que vous devriez savoir. Le type de tuples de C # 7 est en fait la ValueTuple, qui est une structure. Contrairement aux classes, qui sont des types de référence, les structures sont des types de valeur. Vous pouvez en savoir plus à ce sujet sur msdn . Plus important encore, sachez qu’ils peuvent nécessiter beaucoup de copies, soyez donc prudent.

2
andre_ss6

Tuple est une excellente option lorsque vous souhaitez combiner plusieurs valeurs (pouvant être de types différents) dans un même objet sans créer de classe personnalisée. Dans ce cas, Tuple serait une option rapide et parfaite.

1
Qasim Bataineh

Je pense que cela va devenir une question qui se pose souvent. Il n'y a actuellement aucune "bonne pratique" pour savoir quand utiliser la nouvelle valeur tuples vs une classe.

Cependant, cela vaut la peine de lire ce qui est apparu lors de conversations précédentes sur les versions précédentes de tuples vs une classe .

À mon avis, la valeur Tuple ne devrait être utilisée que de manière minimale et avec un maximum de trois valeurs. Je pense que cela fait un bon compromis entre "restituer certaines valeurs sans avoir besoin d'une classe" et "d'horrible fouillis de valeurs". Si vous avez plus de trois valeurs à renvoyer, créez une classe.

Je n'utiliserais jamais non plus un Tuple pour revenir d'une API publique que les consommateurs devront utiliser. Encore une fois, utilisez simplement une classe.

Voici un code du monde réel que j'ai utilisé:

public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()

Dès que je veux renvoyer des données plus complexes, je fais un cours.

0
user9993

J'éviterais d'utiliser des n-uplets comme méthode de retour public. Dans de tels cas, je préférerais définir une classe ou une structure.

0
Frederik Gheysels