martes, 10 de junio de 2014

Expresiones Lambda en C# - Parte 2: Expresiones Lambda Delegados Func y Action de .NET Framework

Tabla de Contenido

0. Introducción
1. Delegados Genéricos Action
1.1 Delegado Action<T>
1.2 Delegado Action<T1, T2>
1.3 Otros delegados Action
2. Delegados Func
2.1 Delegado Fun&ltTResult>
2.2 otros delegados Func
3. Delegados Genéricos y LINQ
4. Conclusiones
5. Glosario
6. Literatura & Enlaces

0. Introducción

En esta segunda parte de la serie de artículos de expresiones lambda en C#, vamos a particularizar con otro tema importante y es el de los delegados genéricos Func y Action integrados en .NET Framework en conjugación con expresiones lambda; conoceremos su utilidad, sus ventajas (en particular, la reutilización).

1. Delegados Genéricos Action

En el namespace System podemos encontrar una interesante lista de delegados genéricos (Tipos Genéricos en C# - Parte 8: Delegados Genéricos) integrados que nos pueden ahorrar varias líneas de código: no tenemos que escribir delegados genéricos personalizados para tareas comunes, debido a que contamos con un conjunto estándar que podemos reutilizar en nuestras soluciones. Este conjunto de delegados genéricos están diseñados para métodos que no retornan ningún valor una vez completada su tarea; ejemplo de esto, eventos (Eventos en C#).

Estudiemos un par de ellos para comprender su utilidad y su ventaja en la reutilización de artefactos de la biblioteca base de clases de Microsoft .NET Framework.

1.1 Delegado Action<T>

Este delegado se haya definido en el nombre de espacios System. Esta es su firma [2]:

public delegate void Action<in T>(T obj)

Descripción:
  • Tipo de retorno void: no retorna ningún tipo de dato.
  • Tipo genérico T: podemos operar con cualquier tipo de datos gracias a su genericidad (calidad de genérico).
Como hemos observado se trata de un delegado genérico que encapsula cualquier método que su firma esté compuesta por único parámetro, y no retorna ningún tipo de valor.

Ejemplo de uso:

Archivo de código fuente UsoActionT.cs [enlace alternativo]:

En la línea 11 declara un delegado genérico -mm- de tipo específico Action<string>, el parámetro de tipo es string, es decir, que aceptamos cualquier método que contenga un único parámetro de tipo string, y que haya especificado como tipo de retorno void. Si el número (1) de argumentos pasados desde la línea de comandos (línea 16), entonces creamos la expresión lambda 

mensaje => MessageBox.Show (mensaje);

en caso contrario (línea 22), recurrimos a la expresión:

mensaje => Console.WriteLine (mensaje);

Tengamos en cuenta que si no hubiéramos utilizado el delegado genérico Action, tuvimos que haber creado un delegado genérico personalizado; por ejemplo:

// Declaración explícita del delegado:
public delegate void MostrarMensaje(string mensaje);

y usarlo de la siguiente manera:

MostrarMensaje mm;

mm += mensaje => MessageBox.Show (mensaje);

Como es evidente este último enfoque requiere más trabajo programático que su alternativa diseñada con delegados genéricos integrados.

1.2 Delgado Action<T1, T2>

Otro ejemplo de delegado genérico integrado es Action<T1, T2> que representa (encapsula) de forma genérica a cualquier método que reciba dos parámetros, y no posea tipo de retorno. Esta es su firma [4]:

public delegate void Action<in T1, in T2>(T1 param1, T2 param2)

Descripción puntual:
  • Parámetros de tipo: T1, T2
  • Parámetros:
    • param1:T1
    • param2:T2
Conozcamos su uso con una declaración y el uso de una expresión lambda:

Archivo de código fuente UsoActionT1T2.cs [enlace alternativo]:

En la línea 15 declaramos una variable de tipo Action<T1, T2> con los tipos particulares string. La primera expresión lambda (línea 21) mapea dos parámetros a la expresión 

Console.WriteLine ("{0}{1}", s1, s2);

La segunda expresión lambda

concatenar = (s1, s2) => EscribirAArchivo(s1, s2);

también mapea dos parámetros (i.e., s1 y s2) e invoca al método pasando como argumentos los dos parámetros mapeados.

Dependiendo del camino que se tome (líneas 19-26), en la línea 28 se invoca se forma indirecta a través del delegado concatenar una de las dos expresiones lambda mencionadas.

Nuevamente vale mencionar que no se ha utilizado ningún delegado genérico personalizado, aunque lo pudimos haber hecho, pero con el integrado nos es suficiente.

Resultado:
Uso delegado genérico Action<T1,T2>
Figura 1. Uso delegado genérico Action<T1, T2>.

1.3 Otros delegados genéricos

.NET Framework cuenta con hasta 17 delegados genéricos integrados que van desde Action hasta Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>.

2. Delegados Génericos Func

Los delegados genéricos integrados Func corresponden con delegados que además de poseer una serie de parámetros también incluyen en su firma un tipo de retorno genérico. Empecemos por explorar una de las versiones más sencillas.

2.1 Delegado Func<TResult>

Esta primera versión de Func encapsula cualquier método que no posee ningún parámetro en la lista de parámetros, pero si un parámetro de tipo como retorno. Esta es su firma [5]:

public delegate TResult Func<out TResult>()

Descripción puntual:
  • Tipo de retorno TResult: el tipo de retorno es un parámetro de tipo.
  • «No posee ningún parámetro de tipo como parámetro. Lista vacía.»
Ejemplo de uso:

Archivo de código fuente UsoFuncTResult.cs [enlace alternativo]:

Observemos este par de detalles:
  1. En la línea 13 usamos el delegado genérico Func<TResult> con el tipo concreto bool para crear una instancia a partir de la expresión lambda

    () => EscribirArchivo(texto);

    Esta expresión lambda no especifica ningún parámetro (debemos usar los paréntesis), y automáticamente se convierte en un closure por capturar la variable exterior texto (declarada y inicializada en la línea 10).
  2. Si no hubiéramos utilizado el delegado genérico integrado, habríamos tenido que escribir el delegado genérico personalizado:

    public delegate TResultado DelegadoEscribir(); 
Respecto al segundo punto, es evidente que recurriremos a la reutilización de artefactos de Microsoft .NET Framework. Esto nos ahorra tiempo y esfuerzos en la escritura de código.

2.2 Otros delegados genéricos Func

Podríamos seguir describiendo y ejemplificando los demás delegados genéricos Func, sin embargo hasta este punto ya debemos tener una comprensión esencial sobre su uso, y utilidad (en lo que respecta a reutilización) con expresiones lambda.

Por otro lado es importante mencionar que .NET Framework cuenta con hasta 17 delegados genéricos que van desde Func<TResult> hasta Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>.

3. Delegados Genéricos y LINQ

A modo de introducción (en la próxima parte de esta serie extenderemos esta sección), los delegados genéricos nos son de excepcional ayuda en la composición de expresiones LINQ.; de ahí que haya decidido extender esta entrega solamente al uso de los delegados genéricos integrados Func y Action (en particular los primeros).

Veamos en breve en que nos puede ayudar la comprensión y uso de los delegados genéricos en LINQ:

List<int> numeros = List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

// Predicado:
Func<int, bool> where = n => n < 6;
// Selección:
Func<int, int> select = n => n;
// Ordenar por:
Func<int, string> orderby = n => n % == 0 ? "even" : "odd";

// Consulta LINQ:
nums = numeros.Where(where).OrderBy(orderby).Select(select);

foreach (var num in nums)
{
Console.WriteLine (num.ToString());
}

Como ya mencioné, en la siguiente entrega nos extenderemos en el uso de expresiones lambda con delegados genéricos para la composición de consultas LINQ.

4. Conclusiones

Conocimos acerca de la utilidad de los delegados genéricos integrados en .NET Framework en conjugación con las expresiones lambda, y concluimos que representan un ahorro significativo en la escritura de código fuente C# y promueve la reutilización de los artefactos integrados. Ya estamos preparados para pasar al siguiente tema que consiste en el uso de expresiones lambda para la composición de consultas LINQ.

Glosario

  • .NET
  • Consulta
  • Delegado
  • Delegado genérico
  • Select
  • LINQ
  • Where

Literatura & Enlaces

[1]: C# 5.0 in a Nutshell by Joseph Albahari and Ben Albahari. Copyright 2012 Joseph Albahari and Ben Albahari, 978-1-449-32010-2.
[2]: Action(T) Delegate (System) - http://msdn.microsoft.com/en-us/library/018hxwa8(v=vs.110).aspx
[3]: Eventos en C# - Parte 1: Introducción a Eventos | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/06/eventos-en-csharp-parte-1-introduccion-a-eventos.html
[4]: Action(T1, T2) Delegate (System) - http://msdn.microsoft.com/en-us/library/bb549311(v=vs.110).aspx
[5]: Func(TResult) Delegate (System) - http://msdn.microsoft.com/en-us/library/bb534960(v=vs.110).aspx
[6]: LINQ (Language-Integrated Query) - http://msdn.microsoft.com/en-us/library/bb397926.aspx


J

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.