C# - Delegados

Descripción general

Es un puntero a una función. Se usa para llamar a una función a través del delgado. La función llamada puede estar en cualquier clase del proyecto. Pero el delegado debe estar declarado y definido en la clase que llama.

[TOC] Tabla de Contenidos


↑↑↑

C# - Delegados


↑↑↑

¿Que son realmente los delegados y los eventos?

Los eventos son señales que emite un objeto al realizar una determinada acción. (Por ejemplo, la pulsación de un determinado botón generara el evento Clic del botón)

El concepto de evento consta de dos partes, el objeto emisor de la señal, el que dispara el evento y el objeto escuchador de la seña, el que escucha el evento y contiene el código necesario para gestionar el evento, para responder de una determinada manera cuando se "escuche" un evento.

Además…, un evento lleva siempre información adicional, valores y parámetros (por ejemplo, que tecla se ha pulsado, coordenadas del ratón en la pantalla al pulsar el botón izquierdo, etc.)

En .NET para definir un evento se define un delegado, Un delegado es un objeto, una clase que tiene un tratamiento y un comportamiento muy especial. Al declarar un delegado hay que asignarle un nombre (terminado en la palabra CallBack), y pasarle los parámetros que necesita que son: primero una Firma de función, (firma deben cumplir todas las funciones que se deban ejecutar al llamar al delegado), y en segundo lugar, almacena en una pila de memoria de direcciones de las funciones para poder ejecutarlas cuando sea preciso.

Para usar un delegado hay que instanciar un objeto y a continuación usarlo.

La gracia de este asunto es que lo que estamos haciendo realmente es llamar a una función, decir a una función que se ejecute, pero esa función puede estar en principio en cualquier clase del proyecto

Por lo tanto al llamar a un evento, se llama en realidad al delegado para que acceda a la pila de direcciones de memoria que tiene registradas y lance (una a una) la ejecución de las funciones almacenas, es decir el código asociado al evento, la función que debe ejecutarse al " escuchar" el evento

La función de los delegados no es únicamente servir de intermediario a los eventos, de hecho la idea de un delegado es disponer de una forma alternativa (y segura) de acceder a cualquier método de cualquier clase (del proyecto)

El uso más común de los delegados son aquellos que permiten ejecutar varias funciones con la misma instrucción, por ejemplo, imagina una fabrica llena de maquinas. Cada máquina tiene su proceso, su función de paro de emergencia. La forma más fácil de parar todas las maquinas es usar un delegado, al que se le pasan todas las funciones [paro de maquinas] de todas las clases y llamarlas a todas con una única instrucción.

Otro uso muy normal es en los subprocesos, para que un hilo comunique al proceso que lo ha lanzado como va su trabajo, si ha terminado, el % ejecutado y cosas así. En este caso el problema que existe es que los hilos se ejecutan es espacios de memoria diferentes, es decir, no se puede acceder a ellos de ninguna forma. La única manera es usar delegados (y / o eventos)


↑↑↑

Declarar un delegado y usar un delegado


↑↑↑

Primero) Las función que será llamada por el delegados

//---------------------------------------
// estas funciones serán llamadas por los delegados
public void F1(string nombre)
{
    System.Windows.MessageBox.Show("Hola " + nombre);
}

↑↑↑

Segundo) Declarar el delegado

/* ---------------------------------------
* Declarar el delegado
* Public --> palabra clave
* Void-> lo que se va a devolver,
*        debe coincidir con la función que se va a llamar
* [DelegadoFuncion] es el nombre del delegado
* (string nombre)-> misma FIRMA que la función que se va a llamar
* (Callback) sufijo propuesto por Microsoft en normas nomenclatura
*/
private delegate void FuncionesCallback(string nombre);

↑↑↑

Tercero) Declarar las variables delegado

/* Declarar las variables delegados a las funciones */
FuncionesCallback d1 = null;

↑↑↑

Cuarto) Instanciar las variables delegado

/* 
 * Instanciar las variables delegado
 * d1-> variable delegado
 * (new Funciones().F1) La función que va a llamar / ejecutar el delegado
 * Variación: (this.Funcion) se está dentro de esta misma clase
 */
d1 = new FuncionesCallback (new Funciones().F1);

↑↑↑

Quinto) Usar el delegado

Forma normal de usar el delegado

d1("1.- Prueba ejecución función F1 a través de la variable delgado d1");

Se pueden combinar delegados, a la hora de instanciarlos (paso cuatro) se hace de la siguiente manera:

DelegadoFunciones d1 = new DelegadoFunciones(new Funciones().F1);
DelegadoFunciones d2 = new DelegadoFunciones(new Funciones().F2);

DelegadoFunciones d3 = (DelegadoFunciones) Delegate.Combine(d1+d2);

↑↑↑

Ejemplo Uno de delegados

Este es un ejemplo sencillo de cómo se declaran y usan delegados.

class Funciones
 {

     // estas funciones seran llamadas por los delegados

     public void F1(string nombre)
     {
         System.Windows.MessageBox.Show("Hola " + nombre);
     }
     public void F2(string nombre)
     {
         System.Windows.MessageBox.Show("Adios " + nombre);
     }

 }


 // esta clase es un ejemplo de uso de delegados
 class ClienteFunciones
 {

     //---------------------------------------
     /*
     * Declarar el delegado
     * Public --> palabra clave
     * Void-> lo que se va a devolver , debe coincidir con la funcion que se va a llamar
     * [DelegadoFuncion] es el nombre del delegado
     * (string nombre)-> misma firma que la funcion que se va a llamar
     */
     private delegate void DelegadoFunciones(string nombre);

     /* Declarar las variables delegados a las funciones */
     DelegadoFunciones d1 = null;
     DelegadoFunciones d2 = null;
     DelegadoFunciones d3 = null;

     public ClienteFunciones()
     {
         /* 
          * Instanciar las variables delegado
          * d1-> variable delegado
          * d2-> variable delegado
          * (new Funciones().F1) La funcion que va a llamar / ejecutar el delegado
          */
         d1 = new DelegadoFunciones(new Funciones().F1);
         d2 = new DelegadoFunciones(new Funciones().F2);

         /*
          * Esta forma es especial, lanza primero el delegado d1 y a continuacion el delegado d2
          * es decir, se ejecuta la funcion F1 y seguidamente la funcion F2
          */
         d3 = (DelegadoFunciones)Delegate.Combine(d1 + d2);
     }

     public void EjemploUsoDelegado()
     {

         d1("1.- Prueba ejecucion funcion F1 a traves de la variable delgado d1");
         d2("2.- Prueba ejecucion funcion F2 a traves de la variable delgado d2");

         // ejecutar las dos funciones f1 y f2 una detras de otra
         d3("3.- Prueba ejecucion funcion F1 y F2 (seguidas) a traves de la variable delgado d3");

     }


     // Metodo principal
     public static void Main()
     {
         ClienteFunciones objClienteFunciones = new ClienteFunciones();
         objClienteFunciones.EjemploUsoDelegado();
     }


 }
 

↑↑↑

Ejemplo DOS de delegados

En este ejemplo se muestra cómo se pueden llamar a varias funciones de varias clases distintas con una única llamada a un delegado

Se supone que estamos en una fabrica con un montón de maquinas

Cada máquina dispone de un método de "Paro de emergencia"

Lo que vamos a hacer es definir un delegado, a su variable de instancia le asignaremos TODAS las funciones "Paro de emergencia" de todas las clases (Maquinas) de forma que con una única llamada al delegado se ejecuten una detrás de otra todas las funciones paro de emergencia de todas las maquinas.

Primero) La función que será llamada por el delegados [StopMaquina}

class PintadoraMaquina : Maquina
{
    // constructor
    public PintadoraMaquina() { /*Constructor de la clase*/}
 
    public override string StopMaquina()
    {
      string cadena;
      cadena = "Parando la maquina " +
        ">" + System.Reflection.MethodBase.GetCurrentMethod().Module.Name +
        ">" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name +
        ">" + System.Reflection.MethodBase.GetCurrentMethod().Name;
 
        //string cadena = "Parando la maquina" + 
        //     System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
        Console.WriteLine(cadena);
        return cadena;
    }
}

Segundo La clase que define el delegado y usa la función [StopMaquina] de la clase anterior

class ClienteMaquinas : IDisposable
 {
 
      #region " Patrón del Delegado"
      // ---------------------------------------------
      // Primero) las funciones que llamara este delegado 
      // En este ejercicio serán la función [StopMaquina] de aquellas 
      // clases que implementen la clase base abstracta [Maquina]
 
      // ---------------------------------------------
      // Segundo) Declarar el delegado'
      public delegate string ParadoMaquinasCallback ();
 
      // ---------------------------------------------
      // Tercero) Declarar las variables delegado
      private ParadoMaquinasCallback paroEmergenciaMaquinas;
 
      // ---------------------------------------------
      // Cuarto 
      //   4.1) Instanciar las variables delegado (Se hace en el constructor)
      //   4.2) Declarar las funciones que ejecutaran las instancias de los 
      //        delegados recién creados ( También se hace en el constructor)
      //        ¡¡Atención!! Se usaran las funciones específicas (4.3)
      //   4.3) Funciones para añadir o quitar las funciones que ejecutan 
      //        Las instancias de los delegados (se llaman en el constructor)
 
      //   4.3.1) Añadir un método al delegado
      public void ParoEmergenciaMaquinasCallbackSuscribirse (
                          ParadoMaquinasCallback metodoQueSeAñade)
      {
          // ¡¡¡ Importante !!! 
          // Aunque la variable delegado [metodoQueSeAñade ] sea null 
          // el operador (+=) inicializa la variable delegado
          paroEmergenciaMaquinas += metodoQueSeAñade;
      }
 
      //   4.3.2) Quitar un método del delegado
      public void ParoEmergenciaMaquinasCallbackQuitarSuscripcion (
                      ParadoMaquinasCallback metodoQueSeQuita)
      {
          if (paroEmergenciaMaquinas != null)
          {
              paroEmergenciaMaquinas -= metodoQueSeQuita;
          }
      }
 
      #endregion
 
  
      // Definición de las maquinas con las que trabaja esta clase
      PintadoraMaquina pintadora = new PintadoraMaquina();
      SoldadoraMaquina soldadora = new SoldadoraMaquina();
      DobladoraMaquina dobladora = new DobladoraMaquina();
 
 
      // constructor
      public ClienteMaquinas()
      {
     //--------------------------------------------------
          // Ojito (A modo de ejemplo)
          // Definir la variable delegado para un único método
          // paroEmergenciaMaquinas = new 
                   ParadoMaquinasCallback(pintadora.StopMaquina);
          //--------------------------------------------------
 
 
          // Cuarto 
          //   4.1) Instanciar las variables delegado
          //       (Se hace AQUÍ, en el constructor)
          //       En realidad no hace falta hacerlo porque el 
          //       proceso de añadir una función al delegado se 
          //       hace en la función [ParoEmergenciaMaquinasCallbackSuscribirse] 
          //       y en ella el operador (+=) instancia la variable 
          //       delegado en el caso que sea null

          //   4.2) Declarar las funciones que ejecutaran las 
          //       instancias de los Delegados recién creados 
          //       se usan las funciones especificas (4.3) 
          //       (Evidentemente tampoco se hace porque se 
          //       encargara de realizarlas el operador (+=) 
          //       de la función [ParoEmergenciaMaquinasCallbackSuscribirse]

          //   4.3) Funciones para añadir o quitar las funciones que ejecutan 
          //        las instancias de los delegados 
          //        (se llaman AQUÍ, en el constructor)
          //         Añadir las funciones de paro de las maquinas al delegado
          ParoEmergenciaMaquinasCallbackSuscribirse (pintadora.StopMaquina);
          ParoEmergenciaMaquinasCallbackSuscribirse (soldadora.StopMaquina);
          ParoEmergenciaMaquinasCallbackSuscribirse (dobladora.StopMaquina);
      }
 

 
      public void TestApagadoMaquinas()
      {
          // Llamar a la variable delgado para que pare las maquinas
          paroEmergenciaMaquinas();
 
          Console.Write("Pulsa una tecla para continuar");
          Console.ReadKey();

          // Este código producirá la siguiente salida
          // Parando la maquina PintadoraMaquina
          // Parando la maquina SoldadoraMaquina
          // Parando la maquina DobladoraMaquina
      }

}

↑↑↑

Ejemplo TRES de delegados

En este ejemplo vamos a ver como se usan los métodos delegados pasados a través de un parámetro.

El ejemplo muestra una calculadora muy sencilla

// Definimos la clase 
// que sirve para hacer operaciones simples
public class OperacionesSimples
{
    // Daclarar el delegado
    // Esta misma firma deberan implementarla las funciones 
    // que seran llamadas por este delegado
    public delegate int OperacionesCallBack(int a, int b);
 
    // Fijate en el parámetro
    public int HacerOperacion(OperacionesCallBack op, int x, int y)
    {
        return (op(x, y));
    }
}
 
public class CalculadoraSimple
{
    // Un Metodo que implementa la firma de OperacionesCallBack
    // y que, porlo tanto, puede ser llamado por una variable delegado
    public static int OperacionSumar(int a, int b)
    {
        return (a + b);
    }
 
    // Un Metodo que implementa la firma de OperacionesCallBack
    public static int OperacionRestar(int a, int b)
    {
        return (a - b);
    }
 
        
    // Metodo principal
    public static void Main()
    {
        // ATENCION. Se crea una instancia del METODO DELEGADO
        OperacionesSimples.OperacionesCallBack sumar = new OperacionesSimples.OperacionesCallBack(OperacionSumar);
        OperacionesSimples.OperacionesCallBack restar = new OperacionesSimples.OperacionesCallBack(OperacionRestar);
 
        // Y ahora creamos una instancia de la clase que contiene el delegado
        OperacionesSimples aritmetica = new OperacionesSimples();
 
        // Y ahora usamos los Metodos delegados
        int resultadoSuma = aritmetica.HacerOperacion(sumar, 665, 1);
        System.Console.WriteLine("Operacion suma  {0} + {1} = {2} ", 655, 1, resultadoSuma);
 
        int resultadoResta = aritmetica.HacerOperacion(restar, 700, 34);
        System.Console.WriteLine("Operacion resta {0} + {1} = {2} ", 700, 34, resultadoSuma);
 
        System.Console.Write("Pulsar cualquier tecla para continuar");
        System.Console.ReadKey();
 
        // Salida que se genera: 
        //  Operacion suma   655 +  1 = 666 ""
        //  Operacion resta  700 - 34 = 666 ""
        //  Pulsar cualquier tecla para continuar
    }

}

↑↑↑

A.2.Enlaces

[Para saber mas]
[Grupo de documentos]
[Documento Index]
[Bloque de apuntes tácticos de C#]
[Documento Start]
[Imprimir el Documento]