Delegados en C#: Expresiones Lambda
Esta es la ultima parte de esta serie veremos las ya muy conocidas Expresiones Lambda.
Definición
Una expresion lambda es:
Una función anónima que se usa para crear delegados.
Esto es importante resaltarlo ya que mucha gente desconoce la relación entre un delegado y una expresión lambda.
Su estructura es:
Parametros => Implementación
- Donde => es el símbolo lambda.
Ejemplo
Tomemos nuestro ejemplo de Funcion Anónima usado en el post del mismo nombre:
Add = delegate(float elNumeroA, float elNumeroB) { return elNumeroA + elNumeroB };
Este seria reescrito como:
Add = (float elNumeroA, float elNumeroB) => elNumeroA + elNumeroB;
Donde float elNumeroA y float elNumeroB serÃan los parámetros de entrada y elNumeroA + elNumeroB serÃan nuestra implementación.
Algo que debemos tener presente con estas expresiones es que el TipoDeValorDevuelto se infiere o “adivina” dependiendo de la forma en que se escribe.
En este caso los tipos son float + float = float por lo que Add tendrá la firma < float,float,float >. (Recuerden leer el post inicial de delegados para ver lo que son las firmas)
Más ejemplos
Comencemos con un ejemplo usando una funcion anónima.
public class Algo { // Definimos un tipo suma delegate double Suma ( float numeroA, float numeroB ); public Algo(){ // Creamos variable del tipo Suma //TipoDeVariable NombreDeVariable Suma Add; //Le asignamos una implementacion mediante una funcion anonima Add = delegate(float elNumeroA, float elNumeroB) { return elNumeroA + elNumeroB }; //La usamos double res = Add(3.0f,5.0f) } }
Reemplacemos el delegado con una expresión lambda.
public class Algo { // Definimos un tipo suma delegate double Suma ( float numeroA, float numeroB ); public Algo(){ // Creamos variable del tipo Suma //TipoDeVariable NombreDeVariable Suma Add; //Usamos la expresion lambda. Add = (float elNumeroA, float elNumeroB => elNumeroA + elNumeroB; //La usamos double res = Add(3.0f,5.0f) } }
Ahora usemos el tipo Generico Func recordando que la firma de suma es < float, float, float >
public class Algo { // Borramos el tipo public Algo(){ // Creamos variable del tipo generico //TipoDeVariable NombreDeVariable Func<float,float,float> Add; //Usamos la expresion lambda. Add = (float elNumeroA, float elNumeroB) => elNumeroA + elNumeroB; //La usamos double res = Add(3.0f,5.0f) } }
Importante: si la firma de la expresion lambda no coincide con la firma esperada tendremos errores a la hora de compilar.
Delegados como parámetros
En el siguiente ejemplo vamos a crear una función que reciba como parametros un delegado que tendra la implementacion de una operación matemática y dos numeros con los que se realizara la operación matemática.
public class Algo { public double Ejecuta(Func<float,float,float> op, float numA, float numB){ return op(numA, numB); } public Algo(){ // Creamos variable del tipo generico //TipoDeVariable NombreDeVariable Func<float,float,float> Add; //Usamos la expresion lambda. Add = (float elNumeroA, float elNumeroB) => elNumeroA + elNumeroB; //La usamos double res = Ejecuta(Add, 3.0f, 5.0f); } }
Y lo divertido es que podemos poner la expresion lambda directamente en la función Ejecuta
public class Algo { public double Ejecuta(Func<float,float,float> op, float numA, float numB){ return op(numA, numB); } public Algo(){ //La usamos double res = Ejecuta((elNumeroA, elNumeroB) => elNumeroA + elNumeroB, 3.0f, 5.0f); } }
Como podrán ver escribimos la expresion lambda sin definir los tipos de los parametros, esto lo podemos hacer gracias a que el método Ejecuta ya define la firma del delegado por lo que el compilador infiere los tipos y valida que sean los correctos, si nosotros cambiaramos el 3.0f o el 5.0f por una cadena de texto el compilador marcaria un error.
Otro punto que se ve claramente es que ahora tenemos todo en una sola linea de código, el uso de lambdas nos ayuda a tener expresiones mas poderosas que optimizan el código.
Expresiones Lambda sin parametros
Para este caso se debe de seguir esta sintaxis:
() => Implementación
El paréntesis a la izquierda es solo para indicar que nuestro delegado no tendrá parametros. Un ejemplo práctico seria:
() => (3 * 6) - 10 ;
Este delegado es un método que regresa un 8 siempre.
Expresiones Lambda con bloques de código
A veces necesitamos que la implementación de nuestro delegado sea algo mas complejo que una simple suma. Para esos casos podemos utilizar un bloque de codigo delimitado por un contexto.
Un contexto es definido por dos llaves, una que abre “{“ y otra que cierra “}“. Las usamos en todas partes y nos sirven para delimitar bloques.
En el caso de las expresiones lambda los contextos se usan para crear algo asi:
(int numA, int numB, int numC) => { int x = numA + numB; return x / numC; }
Podemos notar que ya se usan los “;“ para separar las lÃneas y la palabra return para enviar el valor de retorno, esto es por que al agregar las llaves la implementacion se escribe como lo hariamos con cualquier método siguiendo las mismas reglas.
Notas finales
Lo mas recomendable es usar las lambdas para generar funciones anónimas y los tipos genericos Action y Func en caso que necesitemos usarlos como parametros.
Espero que estos posts ayuden a comprender mejor el uso y evolución de los delegados en la plataforma.net