Compartir a través de


Elección entre tipos de tupla y anónimos

Elegir el tipo adecuado implica considerar su facilidad de uso, rendimiento y desventajas en comparación con otros tipos. Los tipos anónimos están disponibles desde C# 3.0, mientras que los tipos genéricos System.Tuple<T1,T2> se introdujeron con .NET Framework 4.0. Desde entonces se han introducido nuevas opciones con compatibilidad con el nivel de lenguaje, como System.ValueTuple<T1,T2> , que como indica el nombre, proporcionan un tipo de valor con la flexibilidad de los tipos anónimos. En este artículo, aprenderá cuándo es adecuado elegir un tipo sobre el otro.

Facilidad de uso y funcionalidad

Los tipos anónimos se introdujeron en C# 3.0 con expresiones de Language-Integrated Query (LINQ). Con LINQ, los desarrolladores suelen proyectar los resultados de las consultas en tipos anónimos que contienen algunas propiedades seleccionadas de los objetos con los que están trabajando. Considere el ejemplo siguiente, que instancia una matriz de DateTime objetos y los recorre proyectándolos en un tipo anónimo con dos propiedades.

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

Los tipos anónimos se instancian mediante el operador new, y los nombres y tipos de propiedad se deducen de la declaración. Si dos o más inicializadores de objetos anónimos 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 los objetos como instancias del mismo tipo. Comparten la misma información de tipo generada por el compilador.

El fragmento de código de C# anterior proyecta un tipo anónimo con dos propiedades, de forma muy similar a la siguiente clase de C# generada por el compilador:

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 Tipos anónimos. Existe la misma funcionalidad con las tuplas al proyectar en consultas de LINQ; puede seleccionar propiedades en tuplas. Estas tuplas fluyen a través de la consulta, de la misma forma que los tipos anónimos. Ahora considere el siguiente ejemplo utilizando el 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. 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. Además, los System.Tuple tipos son tipos de referencia class . Sin embargo, System.ValueTuple<T1,T2> es un tipo de valor struct. En el fragmento de C# siguiente se usa ValueTuple<string, long> para hacer la proyección. Al hacerlo, asigna mediante una sintaxis literal.

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, vea Tipos de tupla (referencia de C#) o Tuplas (Visual Basic).

Los ejemplos anteriores son todos funcionalmente equivalentes, pero hay pequeñas diferencias en su facilidad de uso y sus implementaciones subyacentes.

Compromisos

Es posible que quiera usar siempre ValueTuple en lugar de Tuple y de los tipos anónimos, pero hay inconvenientes que debería considerar. Los ValueTuple tipos son mutables, mientras que Tuple son de solo lectura. Los tipos anónimos se pueden usar en árboles de expresión; en cambio, las tuplas no. En la tabla siguiente se muestra información general sobre algunas de las principales diferencias.

Principales diferencias

Nombre Modificador de acceso Tipo Nombre de miembro personalizado Apoyo a la deconstrucción Compatibilidad con árboles de expresiones
Tipos anónimos internal class ✔️ ✔️
Tuple public class ✔️
ValueTuple public struct ✔️ ✔️

Serialización

Una consideración importante al elegir un tipo es si es necesario serializar o no. La serialización es el proceso de convertir el estado de un objeto en un formulario que se puede conservar o transportar. Para obtener más información, consulte serialización. Cuando la serialización es importante, se prefiere crear un class o un struct en lugar de tipos anónimos o tipos de tupla.

Rendimiento

El rendimiento entre estos tipos depende del escenario. Un mayor impacto supone la compensación entre las asignaciones y las copias. En la mayoría de los escenarios, el impacto es pequeño. Cuando pudieran surgir impactos importantes, se deben tomar medidas para informar la decisión.

Conclusión

Al elegir entre los tipos de tupla y los anónimos, como desarrollador es necesario tener en cuenta varios factores. 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. Si está trabajando con árboles de expresión y prefiere asignar nombres a las propiedades, elija tipos anónimos. De lo contrario, use Tuple.

Consulte también