Les nouveautés C# 7 (3) : les déconstructeurs

Cet article est le troisième d’une série sur les nouveautés C# 7. Retrouvez les autres :
Nous avons vu dans le premier article de cette série comment alléger notre base de code de toutes ces classes DTO fourre-tout utilisées localement. Nous allons maintenant voir comment également alléger le code côté appelant.
Déconstruire un tuple
Dans les exemples fournis dans le premier article, nous avions écrit ceci :
(int CustomerId, string Prenom, string Nom) customer = GetCustomer(); int id = customer.CustomerId; string firstname = customer.Prenom; string lastname = customer.Nom;
La méthode GetCustomer retourne un tuple contenant 3 propriétés CustomerId, Prenom et Nom, que l’on affecte à une variable locale customer de type tuple(int, string, string). Ensuite, nous déclarons et instancions trois variables locales id, fistname et lastname dans lesquelles nous déversons le contenu du tuple. Nous allons aller plus loin avec la déconstruction.
(int id, string firstname, string lastname) = GetCustomer();
Ici, le contenu du tuple est automatiquement déconstruit dans les 3 variables locales, ce qui nous donne une syntaxe extrêmement allégée. La rédaction aime. Et nous avons deux autres façons d’écrire cette instruction. La première est d’inférer le type de chaque variable :
(var id, var firstname, var lastname) = GetCustomer();
La seconde façon est d’inférer le type de tout le contenu du tuple :
var (id, firstname, lastname) = GetCustomer();
Cette dernière syntaxe est la plus concise, mais également la plus inhabituelle pour un développeur C#. Pour le compilateur, sachez simplement que ces 3 alternatives sont rigoureusement équivalentes. Ma préférence va aujourd’hui pour la première, car j’aime savoir au premier coup d’œil sans Intellisense quel est le type de mes variables, mais reparlons-en dans quelques années quand la dernière se sera démocratisée.
Il est également possible de déconstruire dans des variables préalablement déclarées :
int id; string firstname, lastname; (id, firstname, lastname) = GetCustomer();
Donc voilà pour les tuples. Mais la bonne nouvelle est que la déconstruction est accessible à tous les types C#.
Déconstruire n’importe quelle classe
Dans l’exemple suivant, nous voyons la mise en œuvre de la mécanique de déconstruction :
public class Consumer { public void DoSomething() { var(id, f, l) = Personne.GetJohnDoe(); } } public class Personne { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public void Deconstruct(out int id, out string firstName, out string lastName) { id = Id; firstName = FirstName; lastName = LastName; } public static Personne GetJohnDoe() { return new Personne { Id = 1, FirstName = "John", LastName = "Doe" }; } }
Du côté de l’appelé, pas de changement notable par rapport aux exemples avec les tuples : la classe Personne se déconstruit comme un tuple. Du côté de l’appelant, nous avons dû implémenter une méthode Deconstruct qui retourne dans des paramètres out. “Des paramètres out ???! Mais pourquoi pas des tuples ????” me crierez-vous. Et vous n’auriez pas tout à fait tort. MAIS, de cette façon, nous pouvons écrire des surcharges de déconstruction :
public class Personne { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public void Deconstruct(out int id) { id = Id; } public void Deconstruct(out string firstName, out string lastName) { firstName = FirstName; lastName = LastName; } public void Deconstruct(out int id, out string firstName, out string lastName) { id = Id; firstName = FirstName; lastName = LastName; } public static Personne GetJohnDoe() { return new Personne { Id = 1, FirstName = "John", LastName = "Doe" }; } } public class Consumer { public void DoSomething() { var (id, f, l) = Personne.GetJohnDoe(); var (firstname, lastname) = Personne.GetJohnDoe(); } }
Ceci nous permet d’exposer des stratégies de déconstruction. Plusieurs choses à noter ici, cependant :
- La référence à System.ValueTuple est obligatoire pour pouvoir déconstruire. C’est elle qui autorise la syntaxe de type var (id, first, last).
- Les méthodes de déconstruction ne sont pas facilement “découvrables”. La seule façon est de naviguer dans les méthodes Deconstruct de la classe à déconstruire. Réfléchissez bien avant d’exposer plusieurs surcharges, ce n’est pas forcément simple pour l’appelant.
- S’il est parfaitement possible d’exposer une méthode de déconstruction retournant une seule valeur (ex : Deconstruct(out int id))… il est impossible de la consommer. Le compilateur rejette la déclaration d’un tuple contenant une seule valeur.
Dans l’ensemble, la mécanique de déconstruction est donc très claire et facile à appréhender. On ne peut à ce titre que saluer le boulot de l’équipe de Mads Torgersen qui réussit à enrichir C# sans nuire à sa lisibilité et à la facilité avec laquelle on peut écrire du code.