Tipos de tupla (referência C#)Tuple types (C# reference)

Disponível em C# 7,0 e posterior, o recurso de tuplas fornece uma sintaxe concisa para agrupar vários elementos de dados em uma estrutura de dados leve.Available in C# 7.0 and later, the tuples feature provides concise syntax to group multiple data elements in a lightweight data structure. O exemplo a seguir mostra como você pode declarar uma variável de tupla, inicializá-la e acessar seus membros de dados:The following example shows how you can declare a tuple variable, initialize it, and access its data members:

(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.

Como mostra o exemplo anterior, para definir um tipo de tupla, você especifica os tipos de todos os seus membros de dados e, opcionalmente, os nomes de campo.As the preceding example shows, to define a tuple type, you specify types of all its data members and, optionally, the field names. Você não pode definir métodos em um tipo de tupla, mas pode usar os métodos fornecidos pelo .NET, como mostra o exemplo a seguir:You cannot define methods in a tuple type, but you can use the methods provided by .NET, as the following example shows:

(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.

A partir do C# 7,3, os tipos de tupla dão suporte a operadores de igualdade == e != .Beginning with C# 7.3, tuple types support equality operators == and !=. Para obter mais informações, consulte a seção de igualdade de tupla .For more information, see the Tuple equality section.

Tipos de tupla são tipos de valor; os elementos de tupla são campos públicos.Tuple types are value types; tuple elements are public fields. Isso torna as tuplas tipos de valor mutável .That makes tuples mutable value types.

Observação

O recurso de tuplas requer o System.ValueTuple tipo e os tipos genéricos relacionados (por exemplo, System.ValueTuple<T1,T2> ), que estão disponíveis no .NET Core e .NET Framework 4,7 e posterior.The tuples feature requires the System.ValueTuple type and related generic types (for example, System.ValueTuple<T1,T2>), which are available in .NET Core and .NET Framework 4.7 and later. Para usar tuplas em um projeto que tenha como destino .NET Framework 4.6.2 ou anterior, adicione o pacote NuGet System.ValueTuple ao projeto.To use tuples in a project that targets .NET Framework 4.6.2 or earlier, add the NuGet package System.ValueTuple to the project.

Você pode definir tuplas com um grande número arbitrário de elementos:You can define tuples with an arbitrary large number of elements:

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

Casos de uso de tuplasUse cases of tuples

Um dos casos de uso mais comuns de tuplas é como um tipo de retorno de método.One of the most common use cases of tuples is as a method return type. Ou seja, em vez de definir out parâmetros de método, você pode agrupar resultados de método em um tipo de retorno de tupla, como mostra o exemplo a seguir:That is, instead of defining out method parameters, you can group method results in a tuple return type, as the following example shows:

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);
}

Como mostra o exemplo anterior, você pode trabalhar com a instância de tupla retornada diretamente ou desconstruir -la em variáveis separadas.As the preceding example shows, you can work with the returned tuple instance directly or deconstruct it in separate variables.

Você também pode usar tipos de tupla em vez de tipos anônimos; por exemplo, em consultas LINQ.You can also use tuple types instead of anonymous types; for example, in LINQ queries. Para obter mais informações, consulte escolhendo entre tipos anônimos e de tupla.For more information, see Choosing between anonymous and tuple types.

Normalmente, você usa tuplas para agrupar elementos de dados livremente relacionados.Typically, you use tuples to group loosely related data elements. Isso geralmente é útil em métodos de utilitário privado e interno.That is usually useful within private and internal utility methods. No caso da API pública, considere definir uma classe ou um tipo de estrutura .In the case of public API, consider defining a class or a structure type.

Nomes de campo de tuplaTuple field names

Você pode especificar explicitamente os nomes dos campos de tupla em uma expressão de inicialização de tupla ou na definição de um tipo de tupla, como mostra o exemplo a seguir:You can explicitly specify the names of tuple fields either in a tuple initialization expression or in the definition of a tuple type, as the following example shows:

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}.");

A partir do C# 7,1, se você não especificar um nome de campo, ele poderá ser inferido a partir do nome da variável correspondente em uma expressão de inicialização de tupla, como mostra o exemplo a seguir:Beginning with C# 7.1, if you don't specify a field name, it may be inferred from the name of the corresponding variable in a tuple initialization expression, as the following example shows:

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

Isso é conhecido como inicializadores de projeção de tupla.That's known as tuple projection initializers. O nome de uma variável não é projetado para um nome de campo de tupla nos seguintes casos:The name of a variable isn't projected onto a tuple field name in the following cases:

  • O nome do candidato é um nome de membro de um tipo de tupla, por exemplo,, Item3 ToString ou Rest .The candidate name is a member name of a tuple type, for example, Item3, ToString, or Rest.
  • O nome do candidato é uma duplicata de outro nome de campo de tupla, explícita ou implícita.The candidate name is a duplicate of another tuple field name, either explicit or implicit.

Nesses casos, você especifica explicitamente o nome de um campo ou acessa um campo por seu nome padrão.In those cases you either explicitly specify the name of a field or access a field by its default name.

Os nomes padrão dos campos de tupla Item1 são Item2 , Item3 e assim por diante.The default names of tuple fields are Item1, Item2, Item3 and so on. Você sempre pode usar o nome padrão de um campo, mesmo quando um nome de campo é especificado explicitamente ou inferido, como mostra o exemplo a seguir:You can always use the default name of a field, even when a field name is specified explicitly or inferred, as the following example shows:

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.

As comparações de igualdade de tupla e atribuição de tupla não têm nomes de campo em conta.Tuple assignment and tuple equality comparisons don't take field names into account.

No momento da compilação, o compilador substitui nomes de campo não padrão pelos nomes padrão correspondentes.At compile time, the compiler replaces non-default field names with the corresponding default names. Como resultado, nomes de campo explicitamente especificados ou inferidos não estão disponíveis em tempo de execução.As a result, explicitly specified or inferred field names aren't available at run time.

Atribuição e desconstrução de tuplaTuple assignment and deconstruction

O C# dá suporte à atribuição entre tipos de tupla que atendem às duas condições a seguir:C# supports assignment between tuple types that satisfy both of the following conditions:

  • ambos os tipos de tupla têm o mesmo número de elementosboth tuple types have the same number of elements
  • para cada posição de tupla, o tipo do elemento de tupla à direita é o mesmo que ou implicitamente conversível para o tipo do elemento de tupla esquerdo correspondentefor each tuple position, the type of the right-hand tuple element is the same as or implicitly convertible to the type of the corresponding left-hand tuple element

Os valores de elemento de tupla são atribuídos após a ordem dos elementos de tupla.Tuple element values are assigned following the order of tuple elements. Os nomes dos campos de tupla são ignorados e não atribuídos, como mostra o exemplo a seguir:The names of tuple fields are ignored and not assigned, as the following example shows:

(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

Você também pode usar o operador de atribuição = para desconstruir uma instância de tupla em variáveis separadas.You can also use the assignment operator = to deconstruct a tuple instance in separate variables. Você pode fazer isso de uma das seguintes maneiras:You can do that in one of the following ways:

  • Declare explicitamente o tipo de cada variável dentro dos parênteses:Explicitly declare the type of each variable inside parentheses:

    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.
    
  • Use a var palavra-chave fora dos parênteses para declarar variáveis digitadas implicitamente e deixe o compilador inferir seus tipos:Use the var keyword outside the parentheses to declare implicitly typed variables and let the compiler infer their 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.
    
  • Usar variáveis existentes:Use existing variables:

    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.
    

Para obter mais informações sobre a desconstrução de tuplas e outros tipos, consulte decompondo tuplas e outros tipos.For more information about deconstruction of tuples and other types, see Deconstructing tuples and other types.

Igualdade de tuplaTuple equality

Começando com o C# 7.3, os tipos de tupla oferecem suporte aos operadores == e !=.Beginning with C# 7.3, tuple types support the == and != operators. Esses operadores comparam membros do operando à esquerda com os membros correspondentes do operando à direita após a ordem dos elementos de tupla.These operators compare members of the left-hand operand with the corresponding members of the right-hand operand following the order of tuple elements.

(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

Como mostra o exemplo anterior, as == != operações e não levam em conta os nomes de campo de tupla.As the preceding example shows, the == and != operations don't take into account tuple field names.

Duas tuplas são comparáveis quando as duas condições a seguir são satisfeitas:Two tuples are comparable when both of the following conditions are satisfied:

  • Ambas as tuplas têm o mesmo número de elementos.Both tuples have the same number of elements. Por exemplo, t1 != t2 não compilar se t1 e t2 tiver diferentes números de elementos.For example, t1 != t2 doesn't compile if t1 and t2 have different numbers of elements.
  • Para cada posição de tupla, os elementos correspondentes dos operandos de tupla à esquerda e à direita são comparáveis com os == operadores e != .For each tuple position, the corresponding elements from the left-hand and right-hand tuple operands are comparable with the == and != operators. Por exemplo, (1, (2, 3)) == ((1, 2), 3) não compila porque o 1 não é comparável com (1, 2) .For example, (1, (2, 3)) == ((1, 2), 3) doesn't compile because 1 is not comparable with (1, 2).

Os == != operadores e comparam tuplas na forma de curto-circuito.The == and != operators compare tuples in short-circuiting way. Ou seja, uma operação é interrompida assim que ela atende a um par de elementos não iguais ou atinge as extremidades das tuplas.That is, an operation stops as soon as it meets a pair of non equal elements or reaches the ends of tuples. No entanto, antes de qualquer comparação, todos os elementos de tupla são avaliados, como mostra o exemplo a seguir:However, before any comparison, all tuple elements are evaluated, as the following example shows:

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

Parâmetros de tuplas como saídaTuples as out parameters

Normalmente, você refatoria um método que tem out parâmetros em um método que retorna uma tupla.Typically, you refactor a method that has out parameters into a method that returns a tuple. No entanto, há casos em que um out parâmetro pode ser de um tipo de tupla.However, there are cases in which an out parameter can be of a tuple type. O exemplo a seguir mostra como trabalhar com tuplas como out parâmetros:The following example shows how to work with tuples as out parameters:

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

Tuplas vs System.TupleTuples vs System.Tuple

As tuplas do C#, que são apoiadas por System.ValueTuple tipos, são diferentes das tuplas representadas por System.Tuple tipos.C# tuples, which are backed by System.ValueTuple types, are different from tuples that are represented by System.Tuple types. As principais diferenças são as seguintes:The main differences are as follows:

  • ValueTuple os tipos são tipos de valor.ValueTuple types are value types. Tuple tipos são tipos de referência.Tuple types are reference types.
  • ValueTuple os tipos são mutáveis.ValueTuple types are mutable. Tuple os tipos são imutáveis.Tuple types are immutable.
  • Os membros de dados dos ValueTuple tipos são campos.Data members of ValueTuple types are fields. Os membros de dados dos Tuple tipos são propriedades.Data members of Tuple types are properties.

Especificação da linguagem C#C# language specification

Para obter mais informações, consulte as seguintes notas de proposta de recurso:For more information, see the following feature proposal notes:

Confira tambémSee also