Elección entre tipos de tupla y anónimosChoosing between anonymous and tuple types

A la hora de elegir el tipo adecuado, hay que tener en cuenta su usabilidad, su rendimiento y sus compensaciones en comparación con otros tipos.Choosing the appropriate type involves considering its usability, performance, and tradeoffs compared to other types. Los tipos anónimos están disponibles desde la versión 3.0 de C#, mientras que los tipos System.Tuple<T1,T2> genéricos se introdujeron con .NET Framework 4.0.Anonymous types have been available since C# 3.0, while generic System.Tuple<T1,T2> types were introduced with .NET Framework 4.0. Ya que se han incorporado nuevas opciones con compatibilidad de lenguaje, como System.ValueTuple<T1,T2>, que, como indica su nombre, proporciona un tipo de valor con la flexibilidad de los tipos anónimos.Since then new options have been introduced with language level support, such as System.ValueTuple<T1,T2> - which as the name implies, provide a value type with the flexibility of anonymous types. En este artículo aprenderá cuándo es pertinente elegir un tipo en lugar de otro.In this article, you'll learn when it's appropriate to choose one type over the other.

Usabilidad y funcionalidadUsability and functionality

Los tipos anónimos se introdujeron en la versión 3.0 de C# con expresiones de Language Integrated Query (LINQ).Anonymous types were introduced in C# 3.0 with Language-Integrated Query (LINQ) expressions. Con LINQ, los desarrolladores suelen proyectar los resultados de consultas a tipos anónimos que contienen algunas propiedades concretas de los objetos con los que están trabajando.With LINQ, developers often project results from queries into anonymous types that hold a few select properties from the objects they're working with. Fíjese en el ejemplo siguiente, en el que se crea una instancia de una matriz de objetos DateTime y se itera a través de ellos proyectándose en un tipo anónimo con dos propiedades.Consider the following example, that instantiates an array of DateTime objects, and iterates through them projecting into an anonymous type with two properties.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var anonymous in
             dates.Select(
                 date => new { Formatted = $"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks }))
{
    Console.WriteLine($"Ticks: {anonymous.Ticks}, formatted: {anonymous.Formatted}");
}

Se crea una instancia de los tipos anónimos usando el operador new, y los nombres y tipos de propiedades se infieren a partir de la declaración.Anonymous types are instantiated by using the new operator, and the property names and types are inferred from the declaration. Si dos o más inicializadores de objeto anónimo en el mismo ensamblado especifican una secuencia de propiedades que están en el mismo orden y que tienen los mismos nombres y tipos, el compilador trata el objeto como instancias del mismo tipo.If two or more anonymous object initializers in the same assembly specify a sequence of properties that are in the same order and that have the same names and types, the compiler treats the objects as instances of the same type. Comparten la misma información de tipo generada por el compilador.They share the same compiler-generated type information.

El fragmento de C# anterior proyecta un tipo anónimo con dos propiedades, de forma similar a esta clase de C# generada por el compilador:The previous C# snippet projects an anonymous type with two properties, much like the following compiler-generated C# class:

internal sealed class f__AnonymousType0
{
    public string Formatted { get; }
    public long Ticks { get; }

    public f__AnonymousType0(string formatted, long ticks)
    {
        Formatted = formatted;
        Ticks = ticks;
    }
}

Para obtener más información, consulte los tipos anónimos.For more information, see anonymous types. Existe la misma funcionalidad con las tuplas al proyectar en consultas de LINQ; puede seleccionar propiedades en tuplas.The same functionality exists with tuples when projecting into LINQ queries, you can select properties into tuples. Estas tuplas fluyen a través de la consulta, de la misma forma que los tipos anónimos.These tuples flow through the query, just as anonymous types would. Ahora, tenga en cuenta el ejemplo siguiente usando System.Tuple<string, long>.Now consider the following example using the System.Tuple<string, long>.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var tuple in
            dates.Select(
                date => new Tuple<string, long>($"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks)))
{
    Console.WriteLine($"Ticks: {tuple.Item2}, formatted: {tuple.Item1}");
}

Con System.Tuple<T1,T2>, la instancia expone propiedades de elementos numeradas, como Item1 y Item2.With the System.Tuple<T1,T2>, the instance exposes numbered item properties, such as Item1, and Item2. Estos nombres de propiedades pueden hacer que resulte más complicado entender la intención de los valores de propiedad, ya que el nombre propiedad solo proporciona el ordinal.These property names can make it more difficult to understand the intent of the property values, as the property name only provides the ordinal. Además, los tipos System.Tuple son tipos class de referencia.Furthermore, the System.Tuple types are reference class types. Sin embargo, System.ValueTuple<T1,T2> es un tipo struct de valor.The System.ValueTuple<T1,T2> however, is a value struct type. En el fragmento de C# siguiente se usa ValueTuple<string, long> para hacer la proyección.The following C# snippet, uses ValueTuple<string, long> to project into. Al hacerlo, se asigna mediante una sintaxis literal.In doing so, it assigns using a literal syntax.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var (formatted, ticks) in
            dates.Select(
                date => (Formatted: $"{date:MMM dd, yyyy at hh:mm zzz}", date.Ticks)))
{
    Console.WriteLine($"Ticks: {ticks}, formatted: {formatted}");
}

Para obtener más información sobre las tuplas, consulte Tipos de tupla (referencia de C#) o Tuplas (Visual Basic).For more information about tuples, see Tuple types (C# reference) or Tuples (Visual Basic).

Todos los ejemplos anteriores son equivalentes funcionalmente; sin embargo, hay pequeñas diferencias en cuanto a su usabilidad y sus implementaciones subyacentes.The previous examples are all functionally equivalent, however; there are slight differences in their usability and their underlying implementations.

CompromisosTradeoffs

Es recomendable que use siempre ValueTuple en lugar de Tuple, además de los tipos anónimos, pero hay compensaciones que debería tener en cuenta.You might want to always use ValueTuple over Tuple, and anonymous types, but there are tradeoffs you should consider. Los tipos de ValueTuple son mutables, mientras que los de Tuple son de solo lectura.The ValueTuple types are mutable, whereas Tuple are read-only. Los tipos anónimos se pueden usar en árboles de expresión; en cambio, las tuplas no.Anonymous types can be used in expression trees, while tuples cannot. En la tabla siguiente se muestra información general sobre algunas de las principales diferencias.The following table is an overview of some of the key differences.

Principales diferenciasKey differences

NOMBREName Modificador de accesoAccess modifier TipoType Nombre del miembro personalizadoCustom member name Compatibilidad con la deconstrucciónDeconstruction support Compatibilidad con árboles de expresiónExpression tree support
Tipos anónimosAnonymous types internal class ✔️✔️ ✔️✔️
Tuple public class ✔️✔️
ValueTuple public struct ✔️✔️ ✔️✔️

SerializaciónSerialization

Un elemento importante que hay que tener en cuenta al elegir un tipo es si se tendrá que serializar o no.One important consideration when choosing a type, is whether or not it will need to be serialized. La serialización es el proceso de convertir el estado de un objeto en un formato que se pueda almacenar o transportar.Serialization is the process of converting the state of an object into a form that can be persisted or transported. Para obtener más información, vea el artículo sobre serialización.For more information, see serialization. Cuando la serialización resulta importante, crear un valor class o struct es preferible a los tipos anónimos o los de tupla.When serialization is important, creating a class or struct is preferred over anonymous types or tuple types.

RendimientoPerformance

El rendimiento entre estos tipos depende de cada escenario.Performance between these types depends on the scenario. Un mayor impacto supone la compensación entre las asignaciones y las copias.The major impact involves the tradeoff between allocations and copying. En la mayoría de los escenarios, el impacto es pequeño.In most scenarios, the impact is small. Cuando pudieran surgir impactos mayores, se tendrían que tomar medidas para tomar una decisión fundamentada.When major impacts could arise, measurements should be taken to inform the decision.

ConclusiónConclusion

Al elegir entre los tipos de tupla y los anónimos, como desarrollador es necesario tener en cuenta varios factores.As a developer choosing between tuples and anonymous types, there are several factors to consider. En general, si no trabaja con árboles de expresión y le parece bien usar la sintaxis de tupla, elija ValueTuple, ya que proporcionan un tipo de valor con la flexibilidad de poner nombre a las propiedades.Generally speaking, if you're not working with expression trees, and you're comfortable with tuple syntax then choose ValueTuple as they provide a value type with the flexibility to name properties. Si trabaja con árboles de expresión y prefiere poner nombre a las propiedades, elija los tipos anónimos.If you're working with expression trees, and you'd prefer to name properties, choose anonymous types. En otros casos, use Tuple.Otherwise, use Tuple.

Vea tambiénSee also