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

Leave a Reply

Your email address will not be published. Required fields are marked *