Tuplas-valor en C# 7.0

“… Pero lo nuestro es pasar / Pasar haciendo caminos / Caminos sobre la mar …”
Joan Manuel Serrat, “Cantares” (1969), basada en  un poema de Antonio Machado

La idea de escribir una entrada relacionada con las tuplas-valor (value tuples) añadidas recientemente a C# 7.0 me trajo a la mente el artículo que escribí una vez para la revista dotNetManía con mi maestro y amigo Miguel Katrib cuando aparecieron las tuplas-referencia, aquellas que se sintetizan a través del uso de la clase genérica Tuple<> que fue incorporada a .NET Framework en la versión 4.0. Las nuevas tuplas-valor se introducen casi que con la idea de hacer obsoletas (“todo pasa y todo queda…“) las tuplas-referencia, resolviendo la mayor parte de los inconvenientes que éstas presentan, y algunas ideas en ese sentido ya las proponíamos en aquel lejano artículo (que el lector interesado puede encontrar, gracias a la generosidad de nuestro gran amigo Paco Marín, aquí).

El primer elemento a destacar es que para poder hacer uso de las tuplas-valor es necesario instalar el paquete de NuGet System.ValueTuple, a menos que esté usted desarrollando un proyecto basado en .NET Framework 4.7 o superior, o en .NET Core 2.0 o superior. Como tipo de datos, una diferencia crucial entre ValueTuple y System.Tuple es que el nuevo tipo es una estructura en vez de una clase y sus miembros son campos en vez de propiedades, lo que lo hace en principio más ligero y eficiente con relación a Tuple. Aparte de eso, un somero análisis de los ensamblados correspondientes le convencerá de que ambos tipos utilizan patrones de desarrollo parecidos (incluyendo el truco del “octavo pasajero” al que aludíamos en el artículo antes mencionado) y ofrecen posibilidades similares. Algunas de ellas pueden verse en la parte inicial del ejemplo que se muestra más abajo, aunque también varias diferencias:

  • Si bien por defecto el convenio de nombres para los elementos de una tupla es el mismo (Item1, Item2, etc.), para las tuplas-valor el compilador soporta el uso de nombres alternativos para los campos.
  • Como los elementos de las tuplas-valor son campos públicos, sus valores pueden ser modificados (son mutables).

Pero sin duda alguna lo más útil e interesante en relación con las tuplas-valor es que el compilador de C# las utiliza al transformar el nuevo “azúcar sintáctico” que ahora está soportado en C# 7.0. Dejaré para una próxima entrada un análisis de los “actos de magia” que utiliza el compilador para generar código basado en ValueTuple (que por supuesto no se basa en la herencia, dado que las estructuras no la soportan).

La segunda parte del código a continuación muestra un ejemplo de la que tal vez sea la aplicación más popular para las tuplas-valor: una función que devuelve varios valores. En este caso, el método FibonacciInterval determina simultáneamente el menor número de Fibonacci que es mayor o igual que el valor recibido como parámetro y el siguiente número de la secuencia. Note como se puede también utilizar nombres explícitos para hacer más legible el código que llame a la función:

using System;

namespace ValueTuples
{
    class MainClass
    {
        static void Main(string[] args)
        {
            var t1 = ValueTuple.Create("DH"ValueTuple.Create(27121985));
            var t2 = ("DH", (27121985)); // equivalente "endulzado"
            t1.Item2.Item2 = 10;
            Console.WriteLine(t1.Item2);

            var t3 = (Nombre: "Diana", Nacimiento: (Día: 2, Mes: 4, Año: 1998));
            Console.WriteLine(t3.Nacimiento.Día);

            for (uint i = 0; i < 50; i++)
                Console.WriteLine($"Fibonacci interval for {i} = " +
                    FibonacciInterval(i));
        }

        public static (uint Min, uint Max) FibonacciInterval(uint number)
        {
            (uint min, uint max) = (01);
            while (true)
            {
                if (number < max)
                    return (min, max);
                uint sum = min + max;
                min = max;
                max = sum;
            }
        }
    }
}

Observe también la simpática asignación (uint min, uint max) = (01) al inicio del método. Se trata de un ejemplo de deconstrucción (del inglés deconstruction) o desmontaje de una tupla-valor en sus componentes individuales. Esta característica de deconstrucción (que es más general y puede aplicarse a tipos que no sean tuplas-valor) me recuerda, como ya he dicho antes, al mecanismo de unificación utilizado en el lenguaje Prolog; la abordaré con más detalle en otra futura entrega.


Referencia musical: Tendría yo unos doce años cuando Joan Manuel Serrat fue a cantar a nuestra escuela en las afueras de La Habana, y desde ese entonces lo cuento entre mis favoritos. Adicionalmente, el hecho de que las letras de sus temas más populares de aquellos tiempos estuvieran basadas en poemas de Antonio Machado y Miguel Hernández me incitó a estudiar la vida y obra de esos poetas, sin saber que un día el destino me llevaría a España. ¡Gracias, Maestro!

Octavio Hernandez

Desarrollador y consultor en tecnologías .NET. Microsoft C# MVP entre 2004 y 2010.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *