Types Tuple (référence C#)

La fonctionnalité 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 déclarer une variable 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 l’illustre l’exemple précédent, pour définir un type 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 tuple, mais vous pouvez utiliser les méthodes fournies par .NET, comme l’illustre 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.

Les types tuple prennent en charge les opérateurs== d’égalité et !=. Pour plus d’informations, consultez la section d’égalité de Tuple .

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

Notes

La fonctionnalité tuples nécessite 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 System.ValueTuple NuGet au projet.

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

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’usage les plus courants de tuples est un type de retour de méthode. Autrement dit, au lieu de définir out des paramètres de méthode, vous pouvez regrouper des résultats de méthode dans un type de retour tuple, comme l’illustre 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 l’illustre l’exemple précédent, vous pouvez utiliser l’instance de tuple retournée directement ou la déconstructer dans des variables distinctes.

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

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

Noms des champs tuples

Vous pouvez spécifier explicitement les noms des champs tuples dans une expression d’initialisation de tuple ou dans la définition d’un type tuple, comme l’illustre 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}.");

Si vous ne spécifiez pas de nom de champ, il peut être déduit du nom de la variable correspondante dans une expression d’initialisation de tuple, comme l’illustre 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 tuple dans les cas suivants :

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

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

Les noms par défaut des champs tuple sont Item1, Item3Item2et 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é explicitement ou déduit, comme l’illustre 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.

L’affectation de tuples et les comparaisons d’égalité de 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 spécifiés ou déduits explicitement ne sont pas disponibles au moment de l’exécution.

Conseil

Activez la règle de style de code .NET IDE0037 pour définir une préférence sur les noms de champs tuples déduits ou explicites.

Affectation et déconstruction de tuple

C# prend en charge l’affectation entre les types tuple qui répondent aux deux conditions suivantes :

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

Les valeurs d’élément tuple sont attribuées en suivant l’ordre des éléments de tuple. Les noms des champs tuple sont ignorés et non attribués, comme l’illustre 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’affectation pour déconstructer une instance de tuple dans des variables distinctes. Vous pouvez le faire de l’une des manières suivantes :

  • Déclarez explicitement le type de chaque variable entre 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 des 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.
    
  • Utilisez 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 des tuples et d’autres types, consultez Déconstruction des tuples et d’autres types.

Égalité des tuples

Les types tuple prennent en charge les opérateurs et != les == opérateurs. Ces opérateurs comparent les membres de l’opérande de gauche avec les membres correspondants de l’opérande de droite en suivant l’ordre des éléments tuples.

(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 l’illustre l’exemple précédent, les opérations et != les opérations 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 se compile pas si t1 et t2 a différents nombres d’éléments.
  • Pour chaque position de tuple, les éléments correspondants des opérandes tuples de gauche et de droite sont comparables aux == opérateurs et != aux opérateurs. Par exemple, (1, (2, 3)) == ((1, 2), 3) ne se compile pas, car 1 elle n’est pas comparable à (1, 2).

Les == opérateurs et != les opérateurs comparent les tuples de manière à 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 extrémités des tuples. Toutefois, avant toute comparaison, tous les éléments tuples sont évalués, comme l’illustre 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 sortants

En règle générale, vous refactorisez une méthode qui contient out des paramètres dans une méthode qui retourne un tuple. Toutefois, il existe des cas dans lesquels un out paramètre peut être d’un 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 vs System.Tuple

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

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

spécification du langage C#

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

Voir aussi