Les nouveautés C# 7 (1) : les tuples

Historiquement, les versions de Visual Studio, du framework .NET et de C# ont toujours été synchronisées. Nous sommes progressivement en train de sortir de cet état de fait avec des releases plus fréquentes du framework .NET, mais cela prend du temps. Cette nouvelle mouture de C# sera encore cette fois couplée avec la sortie de Visual Studio 2017. Nous allons parcourir les nouveautés du langage dans une courte série d’articles.
Pour commencer, nous allons aborder les tuples. Les tuples sont des types abstraits encapsulant plusieurs propriétés fortement typées. Elles ont pour but de répondre à une et une seule question : qui n’a jamais voulu retourner plus d’une valeur en retour d’une méthode ?
Les alternatives existent, fonctionnent, mais aucune n’est parfaite :
- des paramètres “out” : d’une part, elles violent les principes de l’orienté objet, car ces paramètres intéressent l’appelant et pas l’appelé. D’autre part, leur usage est interdit lorsqu’on expose une méthode async.
- la déclaration d’une classe ou d’une struct, mais cela fait beaucoup de code peu utile
- utiliser dynamic, mais les impacts en performances sont conséquents, et on perd le typage fort. Embêtant.
- les tuples
Les tuples existent déjà depuis C# 4 et permettent d’écrire ce genre de chose :
public void Main() { var customer = GetCustomer(); int id = customer.Item1; string firstname = customer.Item2; string lastname = customer.Item3; } private Tuple<int, string, string> GetCustomer() { return new Tuple<int, string, string>(1, "John", "Doe"); }
C’est relativement pratique si on doit passer localement plus d’une valeur d’une méthode à une autre. Nous avons bien les avantages d’un typage fort, en contrepartie de propriétés ItemX disgracieuses. Notons que les tuples ne peuvent contenir que des propriétés publiques et mutables.
Tuples non-nommés
Dans C# 7, nous allons voir qu’une grosse couche de sucre syntaxique a été ajoutée, simplifiant fortement l’exposition et la consommation des tuples. Dans VisualStudio 2017 version finale, toutes ces fonctionnalités seront natives, mais à l’heure actuelle, nous devons ajouter le package Nuget System.ValueTuple.
Une fois le package ajouté, nous pouvons dorénavant écrire ce type de choses :
private (int, string, string) GetCustomer() { return (1, "John", "Doe"); }
Dans cet exemple, le langage nous permet de déclarer implicitement un nouveau tuple composé d’un entier et deux chaînes de caractères sous la forme d’une liste de types séparés par des virgules et entourés de parenthèses. Pour instancier un nouveau tuple de ce type, il suffit de passer les valeurs à leur tour séparées par des virgules et entourées de parenthèses. A l’instar d’une méthode anonyme, ces tuples implicites sont des types anonymes.
Et voici ce que le compilateur nous affiche en cas de violation :
Tout développeur ayant tapé plus de 3 lignes de C# devrait se sentir à l’aise avec son utilisation.
La consommation du tuple pour le moment ne change pas. Nous devons toujours consommer le retour de notre méthode en appelant ses propriétés Item1, Item2, etc.
Tuples nommés
Nous pouvons également déclarer une méthode de la façon suivante :
private (int Id, string Firstname, string Lastname) GetCustomer() { return (1, "John", "Doe"); }
Notez que cette fois, les membres sont nommés : Id, Firstname et Lastname. Et nous pouvons maintenant consommer élégamment la méthode GetCustomer :
public void Main() { var customer = GetCustomer(); int id = customer.Id; string firstname = customer.Firstname; string lastname = customer.Lastname; } private (int Id, string Firstname, string Lastname) GetCustomer() { return (1, "John", "Doe"); }
Tout le monde y gagne. La méthode consommatrice a les mêmes avantages qu’avec une classe ou struc classique : propriétés typées et nommées. La méthode appelée n’a pas eu à instancier une classe ou struct. Cela s’annonce extrêmement pratique dans les cas où des classes et structures n’étaient créées que pour être utilisés qu’une fois pour passer un groupe de valeurs d’une méthode à une autre. Et on espère voir arriver rapidement des alternatives aux vilaines méthodes TryParse…
Notons que vous pouvez parfaitement utiliser le typage explicite plutôt que l’inférence avec var :
public void Main() { (int Id, string Firstname, string Lastname) customer = GetCustomer(); int id = customer.Id; string firstname = customer.Firstname; string lastname = customer.Lastname; }
Et rien ne nous oblige à utiliser les mêmes noms de propriétés côté méthode appelante et méthode appelée. Le compilateur se charge de faire le travail de mapping à notre place :
public void Main() { (int CustomerId, string Prenom, string Nom) customer = GetCustomer(); int id = customer.CustomerId; string firstname = customer.Prenom; string lastname = customer.Nom; } public (int Id, string Firstname, string Lastname) GetCustomer() { return (1, "John", "Doe"); }
Si vous utilisiez intensivement les tuples jusqu’ici, pas de crainte à avoir : rien n’empêche de continuer à utiliser les propriétés ItemX, ItemY et ItemZ. De même, ces nouveaux tuples exposent une méthode ToTuple() qui retourne logiquement un tuple classique.
Les recommandations sur l’utilisation de ces nouveaux tuples implicites que pour la classe Tuple historique : utilisez-les à bon escient localement. Si cet ensemble de propriétés est amené à être consommé à plusieurs endroits dans une application, ou à être exposée dans une assembly et consommée dans une autre, privilégiez une classe ou une struct qui feront office de contrat même si techniquement il sera possible d’utiliser des tuples.
contexte local…
bonjour, merci pour cet article très intéressant . J’aurais aimé avoir une explication pour la dernière recommandation : pourquoi ne pas utiliser les tuples hors d’un contrats local ? Merci d’avance
Merci vraiment