Delegates y eventos

Anuncio
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
Delegates y eventos
Delegates
Un delegate es muy similar a un puntero a función de C++, es una estructura de datos
que referencia a un método estático o a un método de instancia de un objeto.
Existen algunas diferencias:
- Un puntero a función de C++ sólo puede referenciar funciones estáticas.
- Un delegate no sólo referencia al punto de entrada del método, sino también a la
instancia del objeto al que pertenece el método.
El sentido de los delegates es permitir que un método pueda recibir como parámetro un
puntero a otro método. Es más, no es necesario conocer en tiempo de compilación el
método concreto que se pasa como parámetro: puede asignarse en tiempo de ejecución,
lo cual ofrece una gran flexibilidad a la hora de programar.
El tipo base de todos los delegates es la clase System.Delegate,que quiere decir que un
delegate es una instancia de la clase System.Delegate (o una derivada).
Los delegates no necesitan saber la clase de objeto al cual referencian. Lo que necesitan
saber es la definición del método al que referencian.
Declaración de Delegates.
Una declaración de un delegate define un tipo de referencia que extiende la clase
System.Delegate y que puede utilizarse para encapsular un método con una definición
determinada..
La sintaxis de la declaración es:
declaración-delegate:
atributosopc
modificadores-del-delegateopc
delegate
identificador(lista-parámetros-formalesopc ) ;
modificadores-del-delegate:
modificador-del-delegate
modificadores-del-delegate modificador-del-delegate
modificador-del-delegate:
new
public
protected
internal
private
Por ejemplo:
delegate int MiDelegate();
Un ejemplo más detallado es el siguiente:
delegate int MiDelegate(int x);
1/11
tipo-resultado
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
class MiClase
{
MiDelegate d = new MiDelegate(Cuadrado);
static float Cuadrado(float x) {
return x * x;
}
static int Cuadrado(int x) {
return x * x;
}
}
Utilización de Delegates.
Una instancia de tipo delegate encapsula un método, también llamado “entidad
invocable”. En el caso de métodos estáticos, una entidad invocable consta únicamente
del método. En el caso de métodos de instancia, una entidad invocable consta de una
instancia de la clase del método y un método de tal instancia.
Un ejemplo de utilización de instancias de tipo delegate es:
using System;
// declaración del delegate
delegate int MiDelegate();
public class MiClase
{
public int MetodoInstancia ()
{
Console.WriteLine("Hola desde el método de instancia.");
return 0;
}
static public int MetodoEstatico ()
{
Console.WriteLine("Hola desde el método estático.");
return 0;
}
}
public class Aplicacion
{
static void Main (string[] args)
{
MiClase p = new MiClase();
// Asociar el delegate al método de instancia:
MiDelegate d = new MiDelegate(p.MetodoInstancia);
// Invocación al método de instancia a través del delegate:
d();
// Asociar el delegate al método estático:
d = new MiDelegate(MiClase.MetodoEstatico);
// Invocación al método estático a través del delegate:
d();
}
}
2/11
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
La salida de este programa es el de la figura 8.1:
Figura 8.1
Delegates Multicast.
Un delegate de tipo multicast puede asociarse a más de un método, de modo que
invocando al delegate una sola vez, todos los métodos a los que está asociado sean
invocados secuencialmente.
Un delegate referencia realmente una lista de métodos a la que es posible añadir o quitar
métodos utilizando los operadores +, +=, - y -=.
Para ilustrar esto puede modificarse el método Main() del ejemplo anterior:
static void Main (string[] args)
{
MiClase p = new MiClase();
// Asociar el delegate al método de instancia:
MiDelegate d = new MiDelegate(p.MetodoInstancia);
// Asociar el delegate al método estático, se utiliza
//el operador + para que se añada el método estático
//a la lista de métodos apuntados por el delegate d:
d += new MiDelegate(MiClase.MetodoEstatico);
// Invocación
//delegate:
de
ambos
d();
}
Obsérvese que sólo se invoca una vez el delegate d().
El resultado se representa en la figura 8.2:
3/11
métodos
a
través
del
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
Figura 8.2
Existe una clase específica para este tipo de delegates multicast llamada
System.MulticastDelegate, la cual deriva de System.Delegate y está en el
assembly Mscorlib (Mscorlib.dll).
La clase Delegate soporta una lista de métodos siempre que todos devuelvan el mismo
tipo de datos, pero en una lista de varios métodos esto no tiene porqué ser así. Cuando el
compilador detecta que un delegate devuelve void crea una instancia de
MulticastDelegate en lugar de una de Delegate.
4/11
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
Eventos.
En su sentido más concreto, un evento es un campo o propiedad de una clase o
estructura. El tipo del evento es delegate, lo cual quiere decir que puede referenciar a
una lista de métodos.
Lo interesante de los eventos es que son utilizados por la clase a la que pertenecen para
notificar que algo ha sucedido a otras clases.
El modelo de eventos en el entorno .NET
El modelo de eventos .NET se basa en los conceptos de productor o fuente de eventos
y consumidor o manejador de eventos.
Figura 8.3. Modelo de eventos en el entorno .NET
El productor o fuente puede generar uno o varios eventos diferentes. El consumidor o
manejador se suscribe a uno o varios eventos del productor para que se le notifique
cuando un evento suceda.
Para poder implementar el mecanismo de suscripción a eventos se utilizan los delegates.
El productor tiene tantas instancias de tipo delegate como eventos pueda producir.
Cuando un consumidor quiere suscribirse a un evento concreto ha de añadir a la lista de
métodos del delegate correspondiente la referencia a un método (del consumidor) que
será el que se invoque a través del delegate cuando se de el evento. Al método cuya
referencia se pasa al delegate se llama método manejador del evento.
El productor o fuente del evento.
La clase que se comporta como fuente de eventos se caracteriza por tener un miembro
de tipo event, el cual se construye a partir de un tipo delegate. Este miembro de tipo
event es el que va a referenciar a la lista de métodos manejadores del evento.
Un ejemplo de clase fuente o productor de eventos puede es:
5/11
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
//Delegate que será utilizado como tipo base del evento MiRefME
public delegate void ReferenciaManejadoresEventos (object fuente,
ArgumentosEvento eventArgs);
//Clase productora o fuente del evento MiRefME
public class MiFuenteEventos
{
//MiRefME es el evento, su tipo está determinado por el delegate
//ReferenciaManejadoresEventos
public event ReferenciaManejadoresEventos MiRefME;
//Este método será llamado por los consumidores de eventos
//para suscribirse al evento
public void AñadirManejador (MiManejadorEventos manejador)
{
//Añade a la lista de métodos manejadores la referencia al
//método ManejadorMiFuenteEventos, el cual ha de existir
//en el objeto manejador y será el método del consumidor
//al que se llame cuando se de el evento MiRefME
this.MiRefME
+=
new
ReferenciaManejadoresEventos
(manejador.ManejadorMiFuenteEventos);
}
//Este método será llamado cuando se desee lanzar el evento
public void LanzarEvento ()
{
//A través de MiRefME se invoca a todos los métodos
//manejadores registrados en la lista referenciada por
//MiRefME.
this.MiRefME
(this,new
ArgumentosEvento
("MiFuenteEventos"));
}
}
El elemento primordial de esta clase es
public event ReferenciaManejadoresEventos MiRefME;
al cual pueden suscribirse los consumidores o manejadores de eventos y a través del
cual pueden ser invocados. El resto, es decir, los métodos de la clase, han sido creados
para facilitar el manejo del evento.
Es importante notar que la estructura de los métodos manejadores, cuyas referencias se
añadirán a la lista de MiRefME, es determinada por el delegate:
public
delegate
void
ReferenciaManejadoresEventos (object fuente,
ArgumentosEvento
eventArgs);
en este ejemplo como puede verse, al invocar a los métodos manejadores del evento se
le pasan dos argumentos (que es el caso más común):
• fuente
representa la clase del objeto que ha generado el evento.
• eventArgs
representa los argumentos del evento.
6/11
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
Los argumentos del evento.
Cuando se notifica un evento a un consumidor, se transmite al consumidor o manejador
cierta información relativa al evento. Piense en un evento “pulsación de tecla” que se
notifique a un formulario. Un dato interesante es el código de la tecla pulsada. A esta
información relativa al evento se le llama argumentos del evento y lo normal es que sea
un objeto de una clase derivada de la clase System.EventArgs.
Siguiendo el ejemplo anterior:
//Al notificar que ha sucedido un evento, lo más común
//es que se desee dar alguna información más.
//Esto puede hacerse utilizando una clase derivada de EventArgs
//en la que se indiquen los argumentos del evento.
public class ArgumentosEvento : EventArgs
{
public string fuenteEvento;
public ArgumentosEvento (string fuenteEvento)
{
this.fuenteEvento = fuenteEvento;
}
}
El consumidor del evento.
El consumidor del evento es un objeto al que se le avisa o notifica que el evento ha
ocurrido. Dicho objeto realiza una acción de respuesta a ese evento (por ejemplo, la
clase Formulario puede querer ser avisada de un evento pulsación de tecla para mostrar
el código de la tecla en una caja de texto).
Para poder realizar la acción de respuesta, la clase consumidora del evento ha de poseer
un método que será invocado cuando se de el evento y cuyo código provocará la
respuesta. Siguiendo con el ejemplo anterior, la clase consumidora o manejadora puede
ser:
//Una clase manejadora del evento MiRefME ha de disponer del método
//ManejadorMiFuenteEventos
public class MiManejadorEventos
{
public void ManejadorMiFuenteEventos (object fuente,
ArgumentosEvento eventArgs)
{
System.Console .WriteLine ("Se ha lanzado un evento");
System.Console.WriteLine
("El
origen
es:
"
+
eventArgs.fuenteEvento);
}
}
El método ManejadorMiFuenteEventos es el que deberá ser invocado cuando se dé el
evento. Para ello ha de estar referenciado por la lista del evento MiRefME (obsérvese que
los
argumentos
que
espera
son
los
que
indica
el
delegate
ReferenciaManejadoresEventos.
7/11
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
Suscripción al evento.
Una vez se dispone de las clases productora o fuente y consumidora o manejadora es
posible crear objetos de ambas y conectarlos entre sí. Al mecanismo de conexión entre
manejador y fuente se le llama “suscripción” y consiste en añadir la referencia al
método manejador del evento a la lista de referencias del evento (MiRefME).
Siguiendo con el ejemplo anterior:
public class Aplicacion {
public static void Main(string [] args)
{
MiFuenteEventos MFE = new MiFuenteEventos ();
MiManejadorEventos MME = new MiManejadorEventos ();
//Suscripción al evento
MFE.AñadirManejador (MME);
...
...
Al pasarle la referencia al objeto MME (MiManejadorEventos) al método
AñadirManejador de la fuente de eventos, se añade la referencia al método
ManejadorMiFuenteEventos en la lista de manejadores del evento.
A partir de ahora, cada vez que se de el evento MiRefME, se llamará al método
ManejadorMiFuenteEventos del objeto MME.
Es importante tener en cuenta que el modelo de eventos es un patrón de comportamiento
que se ha diseñado para manejar la interacción del usuario con el sistema en las
aplicaciones gráficas (aunque no es su única aplicación).
De este modo puede comprenderse que un caso ejemplo es que un objeto Formulario
desee suscribir un método OnClick() al evento click de un objeto de la clase Button.
Así, cuando el usuario pulse el objeto de la clase Button se dará el evento click y se
llamará al método OnClick() del objeto Formulario.
Lanzamiento del evento.
Cuando ocurre el evento ha de invocarse a todos los métodos manejadores suscritos al
evento. En el caso de una aplicación gráfica esta situación puede corresponder a una
pulsación con el ratón por parte del usuario sobre un botón. En este caso, el S.O. es el
encargado de lanzar el evento, es decir, llamar al método OnClick() del objeto
Formulario suscrito al evento click sobre el botón.
En el ejemplo que se está siguiendo un modo de lanzar el evento es:
MFE.LanzarEvento ();
Con lo que la clase aplicación puede quedar como sigue:
public class Aplicacion {
public static void Main(string [] args)
{
MiFuenteEventos MFE = new MiFuenteEventos ();
MiManejadorEventos MME = new MiManejadorEventos ();
//Suscripción al evento
8/11
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
MFE.AñadirManejador (MME);
System.Console.WriteLine ("Si desea lanzar el evento pulse
Enter");
System.Console.ReadLine();
//Lanzamiento del evento
MFE.LanzarEvento ();
}
}
En este caso el evento es lanzado explícitamente cuando el usuario pulsa ENTER.
El ejemplo completo.
El resultado de unir el código comentado es:
using System;
//Al notificar que ha sucedido un evento, lo más común
//es que se desee dar alguna información más.
//Esto puede hacerse utilizando una clase derivada de EventArgs
//en la que se indiquen los argumentos del evento.
public class ArgumentosEvento : EventArgs
{
public string fuenteEvento;
public ArgumentosEvento (string fuenteEvento)
{
this.fuenteEvento = fuenteEvento;
}
}
//Delegate que será utilizado como tipo base del evento MiRefME
public delegate void ReferenciaManejadoresEventos (object fuente,
ArgumentosEvento eventArgs);
//Clase productora o fuente del evento MiRefME
public class MiFuenteEventos
{
//MiRefME es el evento, su tipo está determinado por el delegate
//ReferenciaManejadoresEventos
public event ReferenciaManejadoresEventos MiRefME;
//Este método será llamado por los consumidores de eventos
//para suscribirse al evento
public void AñadirManejador (MiManejadorEventos manejador)
{
//Añade a la lista de métodos manejadores la referencia al
//método ManejadorMiFuenteEventos, el cual ha de existir
//en el objeto manejador y será el método del consumidor
//al que se llame cuando se de el evento MiRefME
this.MiRefME
+=
new
ReferenciaManejadoresEventos
(manejador.ManejadorMiFuenteEventos);
}
//Este método será llamado cuando se desee lanzar el evento
public void LanzarEvento ()
{
9/11
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
//A través de MiRefME se invoca a todos los métodos
//manejadores registrados en la lista referenciada por
//MiRefME.
this.MiRefME
(this,new
ArgumentosEvento
("MiFuenteEventos"));
}
}
//Una clase manejadora del evento MiRefME ha de disponer del método
//ManejadorMiFuenteEventos
public class MiManejadorEventos
{
public void ManejadorMiFuenteEventos (object fuente,
ArgumentosEvento eventArgs)
{
System.Console .WriteLine ("Se ha lanzado un evento");
System.Console.WriteLine ("El origen es: " +
eventArgs.fuenteEvento);
}
}
public class Aplicacion {
public static void Main(string [] args)
{
MiFuenteEventos MFE = new MiFuenteEventos ();
MiManejadorEventos MME = new MiManejadorEventos ();
//Suscripción al evento
MFE.AñadirManejador (MME);
System.Console.WriteLine ("Si desea lanzar el evento pulse
Enter");
System.Console.ReadLine();
//Lanzamiento del evento
MFE.LanzarEvento ();
}
}
Al ejecutarlo se mostrará la siguiente pantalla:
Figura 8.4
10/11
Marco Besteiro y Miguel Rodríguez
Delegates y Eventos
Si se pulsa la tecla ENTER se lanzará el evento:
Figura 8.5
11/11
Descargar