Hace poco hablábamos del nuevo soporte para tuplas de C#, y comentábamos que una forma de consumirlas era mediante la deconstrucción, otra nueva característica introducida en la versión 7 del lenguaje, que consiste en “despiezar” las tuplas, extrayendo de ellas sus elementos e introduciéndolos en variables locales, utilizando una sintaxis muy concisa.
En realidad no es algo demasiado diferente a lo que hacemos normalmente cuando almacenamos en una variable local el resultado de un método que retorna un único valor:
Para que un tipo cualquiera (clase o estructura) pueda ser deconstruido, debe implementar un método
Por último, por si os interesa, os recuerdo otros posts de esta serie sobre novedades de C# 7:
En realidad no es algo demasiado diferente a lo que hacemos normalmente cuando almacenamos en una variable local el resultado de un método que retorna un único valor:
var sum = Sum(1, 3); // Guardamos el valor de retorno en variable localPues bien, cuando un método retorna más de un valor en forma de tupla, podemos conseguir exactamente lo mismo para todos los valores retornados, de una forma igualmente sencilla. El siguiente ejemplo muestra un método que retorna una tupla, y cómo la deconstruimos para obtener sus valores en forma de nuevas variables locales:
Console.WriteLine($"Sum: {sum}");
...
static int Sum(int a, int b)
{
return a+b;
}
var (add, sub, mul, div) = Calculate(1, 3); // Guardamos los valores de retorno en vars localesObservad que hemos utilizado la palabra clave
Console.WriteLine($"Sum: {add}");
...
static (int add, int sub, int mul, int div) Calculate(int a, int b)
{
return (a + b, a - b, a * b, a / b);
}
var
para indicar al compilador que sea él el que se encargue de inferir los tipos de los elementos de la tupla. Pero si nos va el masoquismo podríamos hacerlo de forma mucho más trabajosa; las siguientes líneas son equivalentes:(int add, int sub, int mul, int div) = Calculate(1, 3);También podemos deconstruir sobre variables existentes con anterioridad, como en el siguiente ejemplo:
(var add, var sub, var mul, var div) = Calculate(1, 3);
var (add, sub, mul, div) = Calculate(1, 3);
int add, sub, mul, div;Por supuesto, los nombres de las variables locales pueden ser diferentes a los nombres asignados a los miembros de la tupla. Lo que importa es el orden, que sí debe coincidir (¡recordad que una tupla es una lista ordenada de elementos!):
...
(add, sub, mul, div) = Calculate(1, 3);
Console.WriteLine($"Sum: {add}");
var (a, s, m, d) = Calculate(1, 3);Otro aspecto interesante es que si, como en el ejemplo anterior, no vamos a utilizar todos los elementos de la tupla, podemos sustituir por el placeholder "_" los elementos que no nos interesen:
Console.WriteLine($"Sum: {a}");
var (add, _, _, div) = Calculate(1, 3);
Console.WriteLine($"Sum: {add}, Div: {div}");
La deconstrucción más allá de las tuplas
Aunque hemos visto la deconstrucción en el ámbito de las tuplas, donde tienen una aplicación sencilla y directa, en realidad es una característica del lenguaje que puede utilizarse con cualquier tipo de .NET. No veo que sea algo excesivamente útil en la práctica, e incluso dudo mucho que favorezca la legibilidad del código, pero bueno, siempre es bueno saber que existe esta posibilidad y cómo implementarla.Para que un tipo cualquiera (clase o estructura) pueda ser deconstruido, debe implementar un método
void
llamado Deconstruct()
, más o menos con la siguiente pinta:class PersonEl número y tipo de parámetros
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
public void Deconstruct(out string name, out int age, out string email)
{
name = Name;
age = Age;
email = Email;
}
}
out
en Deconstruct()
es el que define las "piezas" en las que podrán ser deconstruidos los objetos de nuestra clase. Así, según el código anterior, podríamos efectuar una deconstrucción de la siguiente manera:var person = _myServices.GetPersonById(3422);De hecho, si descompilamos un código como el visto anteriormente, descubriremos que, al fin y al cabo, la deconstrucción es un simple edulcorante sintáctico sobre las llamadas al método
...
var (name, age, email) = person;
Console.WriteLine($"I'm {name}, {age} years old");
Console.WriteLine($"Contact me at {email}");
Deconstruct()
:// Decompiled sources:Por este motivo, en la práctica es posible crear distintas sobrecargas de
string name;
string email;
int age;
person.Deconstruct(out name, out age, out email);
Deconstruct()
que serán invocadas en función del número de parámetros que utilicemos en la deconstrucción:class PersonEn definitiva, tenemos delante una vuelta de tuerca al lenguaje que permite sacar mayor partido a las tuplas y facilita la codificación en algunos escenarios. Aunque probablemente no vayamos a usarla todos los días, sin duda es una herramienta interesante para tener en nuestro cinturón de herramientas.
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
// Consumo: var (name, age) = person;
public void Deconstruct(out string name, out int age)
{
name = Name;
age = Age;
}
// Consumo: var (name, age, email) = person;
public void Deconstruct(out string name, out int age, out string email)
{
name = Name;
age = Age;
email = Email;
}
}
Por último, por si os interesa, os recuerdo otros posts de esta serie sobre novedades de C# 7:
- Funciones locales
- Inline out variables
- Tuplas
- Expresiones throw