Delegados en C#: Generics para delegados

En el post anterior descubrimos las funciones anónimas y como crear delegados para poder guardar su referencia y utilizarlas.

Ahora vamos a utilizar un par de tipos que nos facilitarán la vida.

Ya sabemos que un delegado lo podemos crear así:

// Definimos el nuevo tipo
delegate double Suma ( float numeroA, float numeroB );

Pero resulta tedioso el estar creando delegados una y otra cuando tenemos muchas funciones con muchas diferentes firmas, por eso el .Net Framework tiene dos tipos de datos maravillosos que nos ayudaran a evitar realizar esa tarea.

Action<> y Func<>

Son dos tipos de datos Genericos dentro del namespace System.Collection.Generics que pueden almacenar funciones.

Action < T >

Este tipo puede guardar funciones que no regresen valores, por ejemplo tenemos esta función:

void Operacion(string dato){
    Console.WriteLine(dato);
}

ahora la vamos a guardar en un tipo genérico Action de la siguiente forma

Action<string> GuardarOperacion;

GuardarOperacion = Operacion;

Es importante remarcar que Action puede tener uno o mas tipos de datos declarados en su definición ( Action < Tipo1, Tipo2, Tipo3, TipoEtc > ) y deben de coincidir con los de la funcion por ejemplo:

// Tenemos tres tipos en la firma del metodo
void OperacionMultiple(string data, int A, float B){
    // Hace algo ...
}

public static void Main(){
    // Creamos un Action con los tres tipos
    Action<string,int,float> GuardaOperacionMultiple;

    // Como la firma coincide podemos hacer operaciones
    GuardaOperacionMultiple = OperacionMultiple;
}

En este ejemplo vimos como un metodo con tres parametros de entrada con diferentes tipos puede ser referenciado por una variable de tipo Action con la misma firma.

¿Y qué sucede si necesito que mi funcion regrese un valor?

Func < T, TResult >

En ese caso utilizamos Func, tomemos el clásico ejemplo de la suma:

float Suma(int a, int b){
    return a + b;
}

Esta función puede ser guardada en una variable de la siguiente manera:

Func<int,int,float> GuardaSuma;

GuardaSuma = Suma;

Nota: Es importante remarcar que el último tipo declarado en Func es el tipo del valor de retorno.

Otra punto a considerar es cuando tenemos una función que no recibe parametros y regresa un valor como:

int Dato(){
    return 300;
}

En este caso nuestro genérico Func deberá ser declarado asi:

Func<int> GuardaDato;

GuardaDato = Dato;

Entran las funciones anónimas

Conociendo Action y Func ahora podemos darnos vuelo con los delegados de una forma mas limpia, la primer idea que viene a mi mente es una serie de Fibonacci.

using System;

public class Program
{
    public static void Main()
    {
        // Declaramos el Generic
        Func<int,int,int> Fibo = null;

        // Le asignamos la implementacion
        Fibo = delegate (int a, int b){
            Console.WriteLine(a);
            if (a >= 144){
                return a;
            } else {
                // Nos volvemos locos y hacemos recursiidad.
                return Fibo(a + b, a);
            }
        };

        // Iniciamos la ejecucion.
        Fibo(0,1);
    }
}

En este pequeño ejemplo de Fibonacci creamos un Func y después le asignamos mediante un delegado la implementación y dentro de la misma implementacion hacemos que se mande llamar a si mismo.

Al final solo necesitamos iniciar el ciclo recursivo llamando Func.

En el siguiente post veremos que es una expresión lambda y como se usa en frameworks como LinQ.

Leave a Reply

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