Types de tuples (référence C#)

Disponible en C# 7,0 et versions ultérieures, la fonctionnalité de tuples fournit une syntaxe concise pour regrouper plusieurs éléments de données dans une structure de données légère. L’exemple suivant montre comment vous pouvez déclarer une variable de tuple, l’initialiser et accéder à ses membres de données :

(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.

(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.

Comme le montre l’exemple précédent, pour définir un type de tuple, vous spécifiez les types de tous ses membres de données et, éventuellement, les noms de champs. Vous ne pouvez pas définir de méthodes dans un type de tuple, mais vous pouvez utiliser les méthodes fournies par .NET, comme le montre l’exemple suivant :

(double, int) t = (4.5, 3);
Console.WriteLine(t.ToString());
Console.WriteLine($"Hash code of {t} is {t.GetHashCode()}.");
// Output:
// (4.5, 3)
// Hash code of (4.5, 3) is 718460086.

À compter de C# 7,3, les types de tuple prennent en charge les opérateurs d’égalité == et != . Pour plus d’informations, consultez la section égalité des tuples .

Les types de tuples sont des types valeur; les éléments de tuple sont des champs publics. Cela rend les types valeur mutable de tuples.

Notes

La fonctionnalité des tuples requiert le System.ValueTuple type et les types génériques associés (par exemple, System.ValueTuple<T1,T2> ), qui sont disponibles dans .net Core et .NET Framework 4,7 et versions ultérieures. Pour utiliser des tuples dans un projet qui cible .NET Framework 4.6.2 ou version antérieure, ajoutez le package NuGet System.ValueTuple au projet.

Vous pouvez définir des tuples avec un grand nombre d’éléments arbitraires :

var t = 
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26);
Console.WriteLine(t.Item26);  // output: 26

Cas d’usage de tuples

L’un des cas d’utilisation les plus courants de tuples est le type de retour de la méthode. Autrement dit, au lieu de définir des out paramètres de méthode, vous pouvez regrouper les résultats de méthode dans un type de retour de tuple, comme le montre l’exemple suivant :

var xs = new[] { 4, 7, 9 };
var limits = FindMinMax(xs);
Console.WriteLine($"Limits of [{string.Join(" ", xs)}] are {limits.min} and {limits.max}");
// Output:
// Limits of [4 7 9] are 4 and 9

var ys = new[] { -9, 0, 67, 100 };
var (minimum, maximum) = FindMinMax(ys);
Console.WriteLine($"Limits of [{string.Join(" ", ys)}] are {minimum} and {maximum}");
// Output:
// Limits of [-9 0 67 100] are -9 and 100

(int min, int max) FindMinMax(int[] input)
{
    if (input is null || input.Length == 0)
    {
        throw new ArgumentException("Cannot find minimum and maximum of a null or empty array.");
    }

    var min = int.MaxValue;
    var max = int.MinValue;
    foreach (var i in input)
    {
        if (i < min)
        {
            min = i;
        }
        if (i > max)
        {
            max = i;
        }
    }
    return (min, max);
}

Comme le montre l’exemple précédent, vous pouvez utiliser l’instance de tuple retournée directement ou la décomposer dans des variables distinctes.

Vous pouvez également utiliser des types de tuples au lieu de types anonymes. par exemple, dans les requêtes LINQ. Pour plus d’informations, consultez choix entre les types de tuples et anonymes.

En général, vous utilisez des tuples pour regrouper des éléments de données faiblement liés. Cela est généralement utile dans les méthodes utilitaires privées et internes. Dans le cas d’une API publique, envisagez de définir une classe ou un type de structure .

Noms de champs de Tuple

Vous pouvez spécifier explicitement les noms des champs de tuple dans une expression d’initialisation de tuple ou dans la définition d’un type de tuple, comme le montre l’exemple suivant :

var t = (Sum: 4.5, Count: 3);
Console.WriteLine($"Sum of {t.Count} elements is {t.Sum}.");

(double Sum, int Count) d = (4.5, 3);
Console.WriteLine($"Sum of {d.Count} elements is {d.Sum}.");

À compter de C# 7,1, si vous ne spécifiez pas de nom de champ, il peut être déduit à partir du nom de la variable correspondante dans une expression d’initialisation de tuple, comme le montre l’exemple suivant :

var sum = 4.5;
var count = 3;
var t = (sum, count);
Console.WriteLine($"Sum of {t.count} elements is {t.sum}.");

C’est ce que l’on appelle des initialiseurs de projection de Tuple. Le nom d’une variable n’est pas projeté sur un nom de champ de tuple dans les cas suivants :

  • Le nom du candidat est un nom de membre d’un type de tuple, par exemple,, Item3 ToString ou Rest .
  • Le nom du candidat est un doublon d’un autre nom de champ de tuple, explicite ou implicite.

Dans ce cas, vous spécifiez explicitement le nom d’un champ ou vous accédez à un champ par son nom par défaut.

Les noms par défaut des champs de tuple sont Item1 , Item2 , Item3 et ainsi de suite. Vous pouvez toujours utiliser le nom par défaut d’un champ, même lorsqu’un nom de champ est spécifié de manière explicite ou déduite, comme le montre l’exemple suivant :

var a = 1;
var t = (a, b: 2, 3);
Console.WriteLine($"The 1st element is {t.Item1} (same as {t.a}).");
Console.WriteLine($"The 2nd element is {t.Item2} (same as {t.b}).");
Console.WriteLine($"The 3rd element is {t.Item3}.");
// Output:
// The 1st element is 1 (same as 1).
// The 2nd element is 2 (same as 2).
// The 3rd element is 3.

Les comparaisons d’égalité des tuples et des tuples ne prennent pas en compte les noms de champs.

Au moment de la compilation, le compilateur remplace les noms de champs non par défaut par les noms par défaut correspondants. Par conséquent, les noms de champs explicitement spécifiés ou inférés ne sont pas disponibles au moment de l’exécution.

Assignation et déconstruction de Tuple

C# prend en charge l’assignation entre les types tuple qui satisfont les deux conditions suivantes :

  • les deux types de tuples ont le même nombre d’éléments
  • pour chaque position de tuple, le type de l’élément de tuple de droite est le même que ou implicitement convertible en type de l’élément de tuple de gauche correspondant

Les valeurs d’éléments tuples sont assignées à la suite de l’ordre des éléments de Tuple. Les noms des champs de tuple sont ignorés et non assignés, comme le montre l’exemple suivant :

(int, double) t1 = (17, 3.14);
(double First, double Second) t2 = (0.0, 1.0);
t2 = t1;
Console.WriteLine($"{nameof(t2)}: {t2.First} and {t2.Second}");
// Output:
// t2: 17 and 3.14

(double A, double B) t3 = (2.0, 3.0);
t3 = t2;
Console.WriteLine($"{nameof(t3)}: {t3.A} and {t3.B}");
// Output:
// t3: 17 and 3.14

Vous pouvez également utiliser l’opérateur = d’assignation pour déconstruire une instance de tuple dans des variables distinctes. Pour ce faire, vous pouvez procéder de l’une des façons suivantes :

  • Déclarez explicitement le type de chaque variable à l’intérieur des parenthèses :

    var t = ("post office", 3.6);
    (string destination, double distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    
  • Utilisez le var mot clé en dehors des parenthèses pour déclarer les variables implicitement typées et laisser le compilateur déduire leurs types :

    var t = ("post office", 3.6);
    var (destination, distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    
  • Utiliser des variables existantes :

    var destination = string.Empty;
    var distance = 0.0;
    
    var t = ("post office", 3.6);
    (destination, distance) = t;
    Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
    // Output:
    // Distance to post office is 3.6 kilometers.
    

Pour plus d’informations sur la déconstruction de tuples et d’autres types, consultez déconstruction de tuples et d’autres types.

Égalité des tuples

À compter de C# 7.3, les types tuple prennent en charge les opérateurs == et !=. Ces opérateurs comparent les membres de l’opérande de gauche avec les membres correspondants de l’opérande de droite suivant l’ordre des éléments de Tuple.

(int a, byte b) left = (5, 10);
(long a, int b) right = (5, 10);
Console.WriteLine(left == right);  // output: True
Console.WriteLine(left != right);  // output: False

var t1 = (A: 5, B: 10);
var t2 = (B: 5, A: 10);
Console.WriteLine(t1 == t2);  // output: True
Console.WriteLine(t1 != t2);  // output: False

Comme le montre l’exemple précédent, == les != opérations et ne prennent pas en compte les noms de champs de Tuple.

Deux tuples sont comparables lorsque les deux conditions suivantes sont satisfaites :

  • Les deux tuples ont le même nombre d’éléments. Par exemple, t1 != t2 ne compile pas si t1 et t2 ont un nombre d’éléments différent.
  • Pour chaque position de tuple, les éléments correspondants des opérandes de tuple de gauche et de droite sont comparables aux == opérateurs et != . Par exemple, (1, (2, 3)) == ((1, 2), 3) ne se compile 1 pas, car n’est pas comparable à (1, 2) .

Les == != opérateurs et comparent les tuples en mode court-circuit. Autrement dit, une opération s’arrête dès qu’elle rencontre une paire d’éléments non égaux ou atteint les terminaisons des tuples. Toutefois, avant toute comparaison, tous les éléments tuples sont évalués, comme le montre l’exemple suivant :

Console.WriteLine((Display(1), Display(2)) == (Display(3), Display(4)));

int Display(int s)
{
    Console.WriteLine(s);
    return s;
}
// Output:
// 1
// 2
// 3
// 4
// False

Tuples en tant que paramètres de sortie

En règle générale, vous refactorisez une méthode qui a des out paramètres dans une méthode qui retourne un tuple. Toutefois, il existe des cas dans lesquels un out paramètre peut être de type Tuple. L’exemple suivant montre comment utiliser des tuples en tant que out Paramètres :

var limitsLookup = new Dictionary<int, (int Min, int Max)>()
{
    [2] = (4, 10),
    [4] = (10, 20),
    [6] = (0, 23)
};

if (limitsLookup.TryGetValue(4, out (int Min, int Max) limits))
{
    Console.WriteLine($"Found limits: min is {limits.Min}, max is {limits.Max}");
}
// Output:
// Found limits: min is 10, max is 20

Tuples et System.Tuple

Les tuples C#, qui sont sauvegardés par les System.ValueTuple types, sont différents des tuples représentés par les System.Tuple types. Les principales différences sont les suivantes :

  • ValueTuple les types sont des types valeur. Tuple les types sont des types référence.
  • ValueTuple les types sont mutables. Tuple les types sont immuables.
  • Les membres de données de ValueTuple types sont des champs. Les membres de données de Tuple types sont des propriétés.

spécification du langage C#

Pour plus d’informations, consultez les notes de la proposition de fonctionnalités suivantes :

Voir aussi