Delegados en C#. Limpia, encera, limpia, encera.

El delegado es uno de los tipos de dato mas incomprenidos de C# por lo que representan y por como se usan, he decidido aportar mi granito de arena al tema y explicar de la forma mas clara que pueda esta poderosa herramienta del lenguaje.

¿Que es un delegado?

La respuesta mas sencilla para quien viene de C es

Un delegado es un puntero a una función o método.

Una segunda definición ya basada en C# es

Un Tipo de dato que hace referencia a una función o método.

Ambas definiciones son en escencia lo mismo pero vamos dividiendola en partes, tenemos dos elementos principales, el Tipo de dato y la Función o Mètodo.

Función o Mètodo

Una función o método es un bloque de codigo que contiene una serie de sentencias, la hemos utilizado hasta el cansancio desde que aprendimos a programar y que es como esto:

double Suma(float numeroA, float numeroB){
    return numeroA + numeroB;
}

este método lo dividiria en dos partes, la declaración int Suma(int numeroA, int numeroB) y la implementación { return numeroA + numeroB; }. En este momento lo que a nosotros nos importa es la declaración que se divide en tres partes:

TipoDelValorDevuelto NombreDelMetodo (Parametros);

Aqui voy a introducir un nuevo concepto llamado Firma

Firma

La firma de un método es la combinación de los tipos de los parametros y el TipoDelValorDevuelto, en pocas palabras:

Firma = Parametros + TipoDelValorDevuelto

Por ejemplo la firma de nuestro ejemplo anterior seria < float, float, double >. En casos donde la declaración sea:

void HazAlgo(int numA, int numB);

La firma seria algo como < int, int > debido a que el TipoDeValorDevuelto es void y por lo tanto no hay.

La firma es bastante importante por que es la huella digital del método y es la caracteristica que hace compatible a un método con unos delegados e incompatible con otros.

Tipo de dato

Como ya sabemos, los tipos definen el tipo de dato que van a guardar las variables, tenemos tipos por valor como los son int, float, bool, byte y tipos por referencia como lo son la mayoria de las clases en .Net.

En .Net nosotros podemos definir nuevos tipos como usando class para crear clases, enum para crear enumeraciones, struct para estructuras entre otras; esto les sera muy familiar ya que despues de definir mediante estas palabras un nuevo tipo pueden crear una nueva variable, por ejemplo.

// Definimos el nuevo tipo
public class Prueba{
    public int Numero { get; set; }
}

public class Consumir{
    public Consumir(){
        // Creamos variable de tipo Prueba
        Prueba LaPrueba;
        // Le asignamos una instancia
        LaPrueba = new Prueba();
        // La usamos
        LaPrueba.Numero = 10;
    }
}

Volviendo al delegado

Con los delegados pasa lo mismo como veremos a continuacion, tomemos la siguiente declaración:

delegate double Suma ( float numeroA, float numeroB );

Como podemos ver es muy parecido a lo que vimos anteriormente, las diferencias son dos, la primera es la palabra delegate al comienzo de la declaración y la segunda es el ; al final lo que nos indica que no habra implementacion.

Lo que se ha hecho en este lugar es definir un nuevo Tipo para apuntar a métodos, cuyo nombre sera Suma y cuya firma sera < float, float, double > de acuerdo a las reglas que hemos estado viendo.

Esto me permite entonces crear una nueva variable usando el nuevo tipo de la siguiente forma:

public class Algo
{
    // Definimos el nuevo tipo
    delegate double Suma ( float numeroA, float numeroB );

    public Algo(){
        // Creamos variable del tipo Suma
        //TipoDeVariable NombreDeVariable
        Suma              Add;
    }
}

Aqui podemos ver claramente que creamos una nueva variable de nombre Add del tipo Suma. Como es una variable por referencia, el valor de Add es null de la misma forma que cualquier otro tipo por referencia que no ha sido inicializado.

Ahora vamos a crear este método en nuestra clase:

double ImplementacionDeLaSuma ( float elNumeroA, float elNumeroB ){
    return elNumeroA + elNumeroB;
}

¿Pueden decir cual es la firma de este método? . . . . si, es < float, float, double >.
¿Es esta firma igual a la firma de nuestro delegado? . . . . . . si, son iguales.

Entonces aqui viene el truco de magia.

    public Algo(){
        // Creamos variable del tipo Suma
        //TipoDeVariable NombreDeVariable
        Suma              Add;
        //Le asignamos una implementacion 
        Add = ImplementacionDeLaSuma;
    }

Como el método tiene la misma forma que nuestro nuevo tipo, podemos a nuestra nueva variable Add asignarle la implementación de nuestro método debido a que podemos obtener la referencia del metodo mediante su NombreDelMetodo quitando los parametros.

A partir de este momento nosotros podemos utilizar nuestra variable Add como un método debido a que ya esta haciendo referencia al metodo ImplementacionDeLaSuma usandolo de la siguiente forma:

public class Algo
{
    // Definimos el nuevo tipo
    delegate double Suma ( float numeroA, float numeroB );

    public Algo(){
        // Creamos variable del tipo Suma
        //TipoDeVariable NombreDeVariable
        Suma              Add;
        //Le asignamos una implementacion 
        Add = ImplementacionDeLaSuma;
        //La usamos
        double res = Add(3.0f,5.0f)
    }

    double ImplementacionDeLaSuma ( float elNumeroA, float elNumeroB ){
        return elNumeroA + elNumeroB;
    }
}

Hasta este punto descubrimos lo que es crear delegados y usar Funciones con Nombre para interactuar con ellos tambien utilizamos la palabra reservada delegate para crear tipos, descubrimos que era la firma de un método, cual es su declaración y cual es su implementacion.

Para el siguiente post seguiremos con Funciones Anonimas y las cosas se pondran divertidas.

Si tienen dudas preguntas o comentarios pueden dejarlos en los comentarios o buscarme por Twitter.

Leave a Reply

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