Introduction aux tuples en C# en 10 minutes ou moins
[[academy-video-youtube({"vid": "QC6hpl2iU0c", "start_time": "0", "title": "Intro to Tuples in C# In 10 Minutes or Less", "creator": "Tim Corey", "length": "9m 49s"})]]
Les méthodes C# sont conçues autour d'une seule valeur de retour, ce qui fonctionne jusqu'à ce que vous rencontriez un cas où un seul retour n'est vraiment pas suffisant. La validation d'adresse est l'exemple classique : vous devez savoir si l'entrée est valide, et si c'est le cas, vous souhaitez récupérer la forme canonique épurée. Envelopper cette paire dans une nouvelle classe semble lourd pour une méthode, et les paramètres out mélangent l'entrée et la sortie d'une manière qui ne vieillit pas bien. Les tuples ont été ajoutés au langage pour que cette situation exacte ait une réponse plus propre.
Dans sa vidéo "Intro to Tuples in C# In 10 Minutes or Less", Tim Corey travaille avec les tuples en construisant une petite méthode de validation d'adresse qui doit retourner à la fois une chaîne formatée et un indicateur de validité. En chemin, il couvre la syntaxe pour le retour d'un tuple, pourquoi nommer les membres est important, comment diviser le résultat en variables distinctes au site d'appel, et comment ignorer les parties dont vous ne vous souciez pas avec le caractère de rejet. Les lecteurs qui continuent à utiliser des classes de résultats ponctuelles ou des paramètres out trouveront ici un modèle plus léger.
Le problème : Retourner plus d'une valeur
[0:18 - 2:50] Tim commence par une méthode ValidateAddress qui accepte une chaîne d'adresse de rue. Le comportement qu'il souhaite est simple : si l'entrée correspond à "123 Sesame St", retournez le "123 Sesame Street" correctement formaté ; sinon, indiquer que l'adresse est invalide. Deux informations doivent revenir d'un seul appel, la chaîne nettoyée et l'indicateur de succès.
Les options pré-tuple sont toutes deux gênantes. Créer une classe ValidateAddressResult fonctionne, mais cela laisse un type à un seul usage dans le projet qu'aucun autre code n'instanciera jamais. Utiliser un paramètre out pour renvoyer l'adresse formatée via la liste de paramètres fonctionne aussi, mais cela mélange l'entrée et la sortie dans la signature de la méthode et rend le site d'appel plus difficile à lire. Les tuples ont été introduits pour gérer ce cas sans payer ni l'un ni l'autre des coûts.
Retourner un tuple sans nom
[2:50 - 4:01] La syntaxe minimale pour un type de retour de tuple enrobe les deux types entre parenthèses :
public (string, bool) ValidateAddress(string address)
{
if (address == "123 Sesame St")
{
return ("123 Sesame Street", true);
}
return (address, false);
}
public (string, bool) ValidateAddress(string address)
{
if (address == "123 Sesame St")
{
return ("123 Sesame Street", true);
}
return (address, false);
}
La méthode renvoie maintenant une paire (string, bool). Le désavantage apparaît immédiatement lorsque quelque chose essaie de l'utiliser. Sans noms assignés aux membres, IntelliSense les expose comme Item1 et Item2, ce qui ne dit rien au lecteur suivant sur ce que chaque valeur représente. Tim souligne cela comme la raison pour laquelle il ne livre jamais de tuples non nommés même si la syntaxe est la plus courte. Le coût de lecture result.Item2 six mois plus tard est supérieur au coût de taper un nom une fois.
Nommer les membres des tuples
[4:01 - 6:30] Attribuer des noms à chaque membre du tuple est un changement d'une seule ligne dans la déclaration du type de retour :
public (string Address, bool IsValid) ValidateAddress(string address)
{
if (address == "123 Sesame St")
{
return ("123 Sesame Street", true);
}
return (address, false);
}
public (string Address, bool IsValid) ValidateAddress(string address)
{
if (address == "123 Sesame St")
{
return ("123 Sesame Street", true);
}
return (address, false);
}
Maintenant le site d'appel se lit naturellement :
var result = ValidateAddress("123 Sesame St");
if (result.IsValid)
{
Console.WriteLine($"Your validated address is {result.Address}");
}
else
{
Console.WriteLine("That is an invalid address");
}
var result = ValidateAddress("123 Sesame St");
if (result.IsValid)
{
Console.WriteLine($"Your validated address is {result.Address}");
}
else
{
Console.WriteLine("That is an invalid address");
}
Exécuter le code avec "123 Sesame St" imprime la forme canonique. Supprimer le chiffre 1 en tête pour obtenir "23 Sesame St" fait en sorte que IsValid revienne comme faux et imprime le message d'adresse invalide. Le contrat de la méthode est maintenant auto-documenté au site d'appel sans qu'un tuple nommé ne nécessite un fichier de classe de support.
Un tuple peut transporter plus de deux membres. Trois, quatre ou cinq fonctionnent de la même manière. Tim note que qu'une fois qu'un tuple dépasse trois ou quatre membres, un enregistrement ou une classe devient généralement un choix plus clair, mais pour deux ou trois valeurs liées issues d'une seule méthode, le tuple reste l'option la plus légère.
Déconstruction du tuple en variables séparées
[6:30 - 7:39] Parfois, l'appelant préfère conserver chaque élément du résultat dans sa propre variable plutôt que de découper un seul result. C# prend cela en charge directement avec la syntaxe de déconstruction :
(string message, bool valid) = ValidateAddress("123 Sesame St");
if (valid)
{
Console.WriteLine($"Your validated address is {message}");
}
(string message, bool valid) = ValidateAddress("123 Sesame St");
if (valid)
{
Console.WriteLine($"Your validated address is {message}");
}
Les noms de variables du côté gauche ne doivent pas nécessairement correspondre aux noms des membres du tuple dans la méthode. Ici message remplace Address et valid remplace IsValid. Tim souligne que c'est un déconstructeur, pas un destructeur ; les deux mots sonnent similaires mais se réfèrent à des concepts complètement différents. Le déconstructeur divise le tuple en les variables locales que l'appelant veut réellement.
Sauter des valeurs avec le caractère de rejet
[7:41 - 8:54] Lorsque l'appelant ne se soucie que d'un sous-ensemble des valeurs retournées, le caractère de rejet (_) écarte celles dont il n'a pas besoin :
(string message, _) = ValidateAddress("123 Sesame St");
Console.WriteLine(message);
(string message, _) = ValidateAddress("123 Sesame St");
Console.WriteLine(message);
Le trait d'union bas dit au compilateur "J'ignore cette valeur intentionnellement." C'est plus expressif que d'assigner le membre non désiré à un nom de variable lambda comme unused, parce qu'un rejet communique une intention. Tout lecteur de cette ligne sait que l'ignorance de IsValid est un choix délibéré pour ce site d'appel, et non un oubli.
Conclusion : Quand les tuples méritent leur place
[8:54 - 9:28] Le cas d'utilisation principal pour les tuples est de renvoyer plusieurs valeurs à partir d'une méthode sans créer une classe qui n'existe que pour être retournée une fois. Nommer les membres à la signature de la méthode, les décomposer au site d'appel, et rejeter ceux dont l'appelant n'a pas besoin sont les trois modèles qui rendent les tuples confortables à vivre. Au-delà de cela, les tuples sont utiles dans les projections LINQ et les calculs intermédiaires où une paire de valeurs rapide est plus lisible qu'un type anonyme.
Conclusion
[9:28 - 9:49] Les tuples donnent à C# un moyen compact de retourner plus d'une valeur à partir d'une méthode. La forme nommée est la version à utiliser au jour le jour, car result.Address se lit tandis que result.Item1 ne le fait pas. La déconstruction redonne aux pièces des variables locales quand l'appelant préfère cette forme, et le caractère de rejet permet qu'un seul appel ne prenne que ce dont il a besoin.
Conseil pratique : Quand vous commencez à décomposer la même forme de tuple dans trois endroits ou plus, c'est le signal pour passer à un enregistrement à la place. Les tuples brillent quand la forme est locale à une seule méthode ; au moment où la forme devient partie de votre domaine, un type nommé devient rentable.
Regardez la vidéo complète sur sa chaîne YouTube et obtenez plus d'informations sur l'écriture de C# moderne et expressif dans la série de formations de 10 minutes.
