Tipos de tupla (referencia de C#)Tuple types (C# reference)

Disponible en C# 7.0 y versiones posteriores, la característica tuplas proporciona una sintaxis concisa para agrupar varios elementos de datos en una estructura de datos ligera.Available in C# 7.0 and later, the tuples feature provides concise syntax to group multiple data elements in a lightweight data structure. En el siguiente ejemplo se muestra cómo se puede declarar una variable de tupla, inicializarla y acceder a sus miembros de datos: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 se muestra en el ejemplo anterior, para definir un tipo de tupla, se especifican los tipos de todos sus miembros de datos y, opcionalmente, los nombres de campos.As the preceding example shows, to define a tuple type, you specify types of all its data members and, optionally, the field names. No se pueden definir métodos en un tipo de tupla, pero se pueden usar los métodos proporcionados por .NET, como se muestra en el siguiente ejemplo: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 de C# 7.3, los tipos de tupla admiten operadores de igualdad == y !=.Beginning with C# 7.3, tuple types support equality operators == and !=. Para obtener más información, consulte la sección Igualdad de tupla.For more information, see the Tuple equality section.

Los tipos de tupla son tipos de valores; los elementos de tupla son campos públicos.Tuple types are value types; tuple elements are public fields. Esto hace que las tuplas sean tipos de valor mutables.That makes tuples mutable value types.

Nota

La característica de las tuplas requiere el tipo System.ValueTuple y los tipos genéricos relacionados (por ejemplo, System.ValueTuple<T1,T2>), que están disponibles en .NET Core y .NET Framework 4.7 y versiones posteriores.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 en un proyecto que tenga como destino .NET Framework 4.6.2 o versiones anteriores, agregue el paquete NuGet System.ValueTuple al proyecto.To use tuples in a project that targets .NET Framework 4.6.2 or earlier, add the NuGet package System.ValueTuple to the project.

Puede definir tuplas con un gran número arbitrario 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

Uno de los casos de uso más comunes de tuplas es como un tipo devuelto del método.One of the most common use cases of tuples is as a method return type. Es decir, en lugar de definir outlos parámetros del método, puede agrupar los resultados del método en un tipo devuelto de tupla, como se muestra en el ejemplo siguiente: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 se muestra en el ejemplo anterior, puede trabajar directamente con la instancia de la tupla devuelta o deconstruirla en variables independientes.As the preceding example shows, you can work with the returned tuple instance directly or deconstruct it in separate variables.

También puede utilizar tipos de tupla en lugar de tipos anónimos; por ejemplo, en las consultas LINQ.You can also use tuple types instead of anonymous types; for example, in LINQ queries. Para obtener más información, vea Elección entre tipos de tupla y anónimos.For more information, see Choosing between anonymous and tuple types.

Normalmente, se usan tuplas para agrupar elementos de datos relacionados de forma flexible.Typically, you use tuples to group loosely related data elements. Esto suele ser útil en métodos de utilidad privados e internos.That is usually useful within private and internal utility methods. En el caso de la API pública, considere la posibilidad de definir un tipo de clase o de estructura.In the case of public API, consider defining a class or a structure type.

Nombres de campo de tuplaTuple field names

Puede especificar explícitamente los nombres de campo de tupla en una expresión de inicialización de tuplas o en la definición de un tipo de tupla, como se muestra en el siguiente ejemplo: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 de C# 7.1, si no se especifica ningún nombre de campo, se puede deducir del nombre de la variable correspondiente en una expresión de inicialización de tupla, como se muestra en el siguiente ejemplo: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}.");

Esto se conoce como "inicializadores de proyección de tupla".That's known as tuple projection initializers. El nombre de una variable no se proyecta en un nombre de campo de tupla en los siguientes casos:The name of a variable isn't projected onto a tuple field name in the following cases:

  • El nombre del candidato es un nombre de miembro de un tipo de tupla, por ejemplo, Item3, ToString o Rest.The candidate name is a member name of a tuple type, for example, Item3, ToString, or Rest.
  • El nombre del candidato es un duplicado de otro nombre de campo de tupla, ya sea explícita o implícita.The candidate name is a duplicate of another tuple field name, either explicit or implicit.

En estos casos, se especifica el nombre de un campo o se accede a un campo por su nombre predeterminado.In those cases you either explicitly specify the name of a field or access a field by its default name.

Los nombres predeterminados de los campos de tupla son Item1, Item2, Item3, etc.The default names of tuple fields are Item1, Item2, Item3 and so on. Siempre puede usar el nombre predeterminado de un campo, incluso cuando se especifica un nombre de campo de forma explícita o inferida, como se muestra en el siguiente ejemplo: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.

En la asignación de tuplas y las comparaciones de igualdad de tuplas no se tienen en cuenta los nombres de campo.Tuple assignment and tuple equality comparisons don't take field names into account.

En el tiempo de compilación, el compilador sustituye los nombres de campo no predeterminados por los nombres predeterminados correspondientes.At compile time, the compiler replaces non-default field names with the corresponding default names. Como resultado, los nombres de campo especificados o inferidos no están disponibles en el tiempo de ejecución.As a result, explicitly specified or inferred field names aren't available at run time.

Asignación y deconstrucción de tuplasTuple assignment and deconstruction

C# admite la asignación entre tipos de tupla que satisfacen estas dos condiciones:C# supports assignment between tuple types that satisfy both of the following conditions:

  • ambos tipos de tupla tienen el mismo número de elementos;both tuple types have the same number of elements
  • para cada posición de tupla, el tipo de elemento de tupla de la derecha es el mismo que el tipo de elemento de tupla de la izquierda correspondiente, o bien puede convertirse a este.for 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

Los valores de elementos de tupla se asignan siguiendo el orden de los elementos de tupla.Tuple element values are assigned following the order of tuple elements. Los nombres de los campos de tupla se omiten y no se asignan, como se muestra en el siguiente ejemplo: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

También puede usar el operador de asignación = para deconstruir una instancia de tupla en variables independientes.You can also use the assignment operator = to deconstruct a tuple instance in separate variables. Para ello, siga uno de estos métodos:You can do that in one of the following ways:

  • Declarar explícitamente el tipo de cada variable entre paréntesis: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.
    
  • Usar la palabra clave var fuera de los paréntesis para declarar las variables con tipo implícito y permitir que el compilador deduzca sus 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 variables 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 obtener más información sobre la deconstrucción de tuplas y otros tipos, consulte Deconstrucción de tuplas y otros tipos.For more information about deconstruction of tuples and other types, see Deconstructing tuples and other types.

Igualdad de tuplaTuple equality

Comenzando con C# 7.3, los tipos de tupla admiten los operadores == y !=.Beginning with C# 7.3, tuple types support the == and != operators. Estos operadores comparan los miembros del operando izquierdo con los miembros correspondientes del operando derecho, siguiendo el orden de los elementos de la 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 se muestra en el ejemplo anterior, las operaciones == y != no tienen en cuenta los nombres de campo de tupla.As the preceding example shows, the == and != operations don't take into account tuple field names.

Dos tuplas son comparables cuando se cumplen estas dos condiciones:Two tuples are comparable when both of the following conditions are satisfied:

  • Ambas tuplas tienen el mismo número de elementos.Both tuples have the same number of elements. Por ejemplo, t1 != t2 no se compila si t1 y t2 tienen números diferentes de elementos.For example, t1 != t2 doesn't compile if t1 and t2 have different numbers of elements.
  • Para cada posición de tupla, los elementos correspondientes de los operandos de la tupla de la izquierda y de la derecha son comparables con los operadores == y !=.For each tuple position, the corresponding elements from the left-hand and right-hand tuple operands are comparable with the == and != operators. Por ejemplo, (1, (2, 3)) == ((1, 2), 3) no se compila porque 1 no es comparable con (1, 2).For example, (1, (2, 3)) == ((1, 2), 3) doesn't compile because 1 is not comparable with (1, 2).

Los operadores == y != comparan las tuplas en modo de cortocircuito.The == and != operators compare tuples in short-circuiting way. Es decir, una operación se detiene en cuanto da con un par de elementos que no son iguales o alcanza los extremos de las tuplas.That is, an operation stops as soon as it meets a pair of non equal elements or reaches the ends of tuples. Sin embargo, antes de cualquier comparación, se evalúan todos los elementos de tupla, como se muestra en el siguiente ejemplo: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

Tuplas como parámetros de salidaTuples as out parameters

Normalmente, se refactoriza un método que tiene parámetros de out en un método que devuelve una tupla.Typically, you refactor a method that has out parameters into a method that returns a tuple. Sin embargo, hay casos en los que un parámetro de out puede ser de un tipo de tupla.However, there are cases in which an out parameter can be of a tuple type. En el siguiente ejemplo básico se indica cómo trabajar con tuplas como parámetros de out: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 frente a System.TupleTuples vs System.Tuple

Las tuplas de C#, que están respaldadas por tipos de System.ValueTuple, son diferentes de las tuplas representadas por tipos de System.Tuple.C# tuples, which are backed by System.ValueTuple types, are different from tuples that are represented by System.Tuple types. Las diferencias principales son las siguientes:The main differences are as follows:

  • Los tipos de ValueTuple son tipos de valores.ValueTuple types are value types. Los tipos de Tuple son tipos de referencia.Tuple types are reference types.
  • Los tipos de ValueTuple son mutables.ValueTuple types are mutable. Los tipos de Tuple son inmutables.Tuple types are immutable.
  • Los miembros de datos de tipos de ValueTuple son campos.Data members of ValueTuple types are fields. Los miembros de datos de tipos de Tuple son propiedades.Data members of Tuple types are properties.

Especificación del lenguaje C#C# language specification

Para obtener más información, consulte las siguientes notas de propuestas de características:For more information, see the following feature proposal notes:

Vea tambiénSee also