jueves, 3 de diciembre de 2009

Delegados C#

Un delegado o delegate es un tipo que hace referencia a un método.Cualquier método que coincida con la firma del delegado puede asignarse al delegado.Veamos un ejemplo:


//Declaro un delegado
public delegate int DelCalculo(int a,int b);

//Creo un Metodo para el delegado DelCalculo.
/*Nótese que coincide con la firma del delegado,
*tanto por el tipo de valor devuelto como por los
*tipos de los parametros*/
public static int Suma(int a, int b)
{
return (a + b);
}

/******************************************************************************/
/*Creo una instancia del delegado asignándole
* el metodo al que hará referencia*/
DelCalculo obj = Suma;

/*Llamo al delegado*/
int val = obj(3, 4);

/*Muestro en pantalla el valor devuelo:
*El valor devuelto es : 7 */
Console.WriteLine("El valor devuelto es : {0}",val);


Desde la versión 2.0 de C# se puede hacer uso de los métodos anónimos,esto es pasar un bloque de código como parámetro de delegado, de esta forma no tendría que crear un método para asignárselo en la instanciación del delegado como lo hice en el código mostrado lineas arriba.A continuación instaciaré al delegado usando un método anónimo y en vez de devolver la suma de los parámetros, devolverá la diferencia:


/*Creo instancia del delegado usando un metodo anonimo*/
DelCalculo obj2 = delegate(int a, int b) { return (a - b); };

/*llamo al delegado*/
val = obj2(10, 5);

/*Muestro en pantalla el valor devuelto:
*El valor devuelto por obj2 es : 5 */
Console.WriteLine("El valor devuelto por obj2 es : {0}", val);


Desde la versión 3.0 del framework .net es posible hacer uso de expresiones Lambda.A continuación instaciaré al delegado usando expresiones lambda y retornará el producto de sus parametros :


/*instanciando delegado usando expresiones lambda*/
DelCalculo obj3 = (int a, int b) => { return (a * b); };

/*llamando al delegado*/
val = obj3(6, 8);

Console.WriteLine("El valor devuelto por obj3 es : {0}", val);

/*Salida en pantalla: El valor devuelto por obj3 es : 48 */


Dado que un delegado con instancias es un objeto, se puede pasar como parámetro a un metodo para ser llamado posteriormente, lo que se conoce como devolución de llamada asincrónica.Veamos un ejmplo:


/*Utiliza el delegado DelCalculo como parámetro y lo invoca*/
public static void MetodoConCallBack(int x, int y, DelCalculo callback)
{
int val = callback(x,y);
Console.WriteLine("El resultado es : {0}",val);
}

/*llamo al método pasándole como parámetro una instancia del
* delegado DelCalculo, en este caso: obj*/

MetodoConCallBack(5, 4, obj);

/*La salida en pantalla será: El resultado es : 9 */


Un delegado puede llamar a mas de un método cuando se invoca.Sólo es necesario agregar los delegados -que llaman a los otros metodos- a su lista de invocación mediante el operador de suma "+" o "+=".Para el siguiente ejemplo crearé una nueva clase que contendrá dos metodos y tambien crearé un nuevo delegado que hará referencia a esos metodos:


using System;

namespace TestingDelegates
{
class MisMetodos
{
public void Metodo1(string cad)
{
Console.WriteLine(cad+" : Metodo1" );
}

public void Metodo2(string cad)
{
Console.WriteLine(cad + " : Metodo2");
}
}
}

/***********************************************************/

/*instancia de clase MisMetodos*/
public static MisMetodos metodos = new MisMetodos();
/*Delegado para metodos de MisMetodos*/
public delegate void nuevoDel(string cad);

/*asignando*/
nuevoDel del1 = metodos.Metodo1;
nuevoDel del2 = metodos.Metodo2;

nuevoDel contenedorDel = del1 + del2;

/*Invocando al delegado que llamará a los métodos*/
contenedorDel("Metodo ejecutado");

/*La salida será :
*Metodo ejecutado : Metodo1
*Metodo ejecutado : Metodo2*/


También se pueden quitar métodos de la lista de invocación.A continuación quitaré el método Metodo1:


/*quitando el metodo Metodo1 de la lista de invocación*/
contenedorDel -= metodos.Metodo1;

contenedorDel("Metodo ejecutado");

/*La salida será:
*Metodo ejecutado : Metodo2 */


Al principio decía que el método debe coincidir con la firma del delegado, en realidad no tienen que coincidir exactamente, esto se debe a la covarianza y contravarianza:
La covarianza permite que un método tenga un tipo de valor devuelto más derivado que lo que se define en el delegado. La contravarianza permite un método con tipos de parámetro que se deriven menos que en el tipo de delegado.

Veamos:


/*Tengo dos clases, una base y otra derivada:*/
public class ClasePadre
{

}

public class ClaseHija : ClasePadre
{

}

/***********************************************************************/

/*declaro un delegado para mostrar la Covarianza*/
public delegate ClasePadre CovarianzaDel();

/*declaro un delegado para mostrar la Contravarianza*/
public delegate void ContravarianzaDel(ClasePadre padre);

/************************************************************************/
//USANDO EXPRESIONES LAMBDA:

/*la covarianza nos permite esto:
*devolver un tipo derivado del tipo que especifica el delegado*/
CovarianzaDel cov = () => { return new ClaseHija(); };



ContravarianzaDel contrav = (ClasePadre padre) => {/*...*/};

ClaseHija hija = new ClaseHija();

/*La contravarianza nos permite mandar como parametro una
*instancia de la clase derivada
*de la que se especifica en el delegado*/

contrav(hija);

No hay comentarios:

Publicar un comentario