miércoles, 4 de junio de 2014

Eventos en C# - Parte 1: Introducción a Eventos

Índice

0. Introducción
1. Eventos
1.1 Concepto Genérico
1.2 Concepto en C#
1.3 La palabra clave event
1.2.1 Ejemplo concreto
1.2.2 Modificadores de event
2. Funcionamiento y Uso de Eventos (Sección de Extensión)
2.1 Funcionamiento interno de los eventos
2.2 Ejemplos de uso
2.2.1 Metrónomo
2.2.2 Divisor
3. Conclusiones
4. Glosario
5. Literatura & Enlaces

0. Introducción

Empiezo esta nueva serie de artículos (que serán 3, precisamente) en donde tendremos la oportunidad de entrar en nuevo tema: eventos (events, en inglés). Veremos las posibilidades que brindan a nuestras aplicaciones que requieran un modelo basado en publicador-subscritor. En esta primera parte vamos a hacer una introducción general para poder entender y aplicar el concepto de evento. En la segunda nos enfocaremos en el patrón estándar de eventos, y en la última parte, estudiaremos eventos modificadores y eventos de consulta (getters o accessors).

1. Eventos

1.1 Concepto Genérico

Un evento consiste en una acción generada a partir de la interacción con una aplicación: clic de mouse, pulsación de tecla o botón, selección de comando de un menú, arrastrar y soltar elementos en pantalla, &c. Los eventos también puede generarse por la generación de la actividad de un sistema información: por ejemplo en un sistema de cajeras automáticos (ATM), una vez que un cliente de la entidad bancaria realiza una transacción, el sistema se encarga de hacer un registro de la actividad para controlar y asegurar los valores de los clientes de la entidad, igualmente, el mismo sistema puede enviar un mensaje de texto al móvil del cliente con el registro (lugar, valor, fecha y hora) de la transacción recién efectuada. (En la Figura 1 se puede apreciar pictóricamente todos los eventos anteriores.)
Eventos en un sistema informático cotidiano
Figura 1. Eventos en un sistema informático cotidiano.

1.2 Concepto en C#

Básicamente, un evento en C# es un mecanismo que permite la generación de notificaciones a un conjunto de clientes cuando algún suceso interesante transcurra sobre un objeto. Sucesos a los que podríamos distinguir como interesantes (ejemplos):
  • Cambio del estado del objeto.
  • Clic sobre un botón de envío de información de un formulario.
  • Descarga de archivo completada.
  • Notificación sobre con un globo de texto cuando la sincronización de un archivo se ha completado.
  • Porcentaje del avance de un proceso sobre una barra de progreso.
  • Cambio del nombre del archivo afecta el texto de la barra de título de una aplicación.
Todos los anteriores, y muchos otros eventos son generados a partir de una clase publicadora y notificados a un conjunto de clases clientes (o subscriptores) que están interesados en la generación de esos cambios producidos por los eventos.

En la programación de interfaces gráfica de usuario es común el uso de eventos. Los eventos se asocian con controles (componentes gráficos: botones, listas, cajas de comprobación, áreas de texto, agrupador de elementos, &c.). Cada vez que un usuario pulsa un botón, por ejemplo, se dispara un evento que genera una acción particular (guardar archivo, cerrar el programa, ir a la siguiente página, etc.).
Eventos en una interfaz gráfica de usuario
Figura 2. Eventos en una interfaz gráfica de usuario.

Aparte de la utilidad anterior de los eventos, éstos también son usados para cualquier escenario que requiera la notificación de cambios de estados de un objeto a un conjunto de clientes interesados en conocer su estado y reaccionar frente a esos cambios.

Por otra parte, en la serie de artículos «Delegados en C#» aprendimos a declarar, usar y crear programas usando delegados (Delegados en C# - Parte 1: Introducción). Saber en qué consiste delegados nos permitirá avanzar más fácilmente en esta serie de «Eventos en C#», debido a que un evento es un tipo de delegado especial (por especificarlo de algún modo).

Más técnicamente, un evento es un mecanismo utilizado por una clase para exponer una serie de delegados de métodos, los cuales son invocados una vez ocurra el evento asociado a esa clase de objeto.

1.3 Palabra clave event

La palabra clave event se usa para hacer que una clase sea tratada como la generadora de eventos. Se usa de la siguiente manera:

modificador-acceso event TipoDelegado identificador;

Descripción puntual:
  • modificador-acceso: cualquiera de los modificadores disponibles en C# (Modificadores de acceso en C#).
  • event: palabra reservada de C#.
  • TipoDelegado: tipo de delegado de la biblioteca de clases de Microsoft .NET Framework o uno definido por el programador.
  • identificador: nombre o identificador del evento.
Ejemplo específico:

Declaramos un delegado:

public delegate void CalcularEventHandler(int a, int b);

Especificamos un evento a partir de CalcularEventHandler (EventHandler es un postfijo para aludir que es un delegado diseñado para un evento):

public event CalcularEventHandler calculo;

1.3.1 Ejemplo concreto

En el siguiente ejemplo vamos a crear un delegado compatible con métodos de asignación de números. Luego crearemos un evento asociado al delegado previo, para notificar los cambios que se hagan sobre el campo de una clase publicadora.

Archivo de código fuente EjemploEvento.cs [enlace alternativo]:
En la línea 13 creamos el delegado con tipo de retorno void y cero parámetros: ManipulacionCampoValorEventHandler. Podemos asociar métodos compatibles con este delegado. Los métodos asociados se activarán cuando el evento declarado de este mismo tipo de delegado se dispara:

public event ManipulacionCampoValorEventHandler CambioNumero;
Cada vez que se invoque al método EstablecerValor se hará la invocación implícita a todos los métodos asociados con la ocurrencia del evento CambioNumero. (Tradicionalmente, se ha usado el prefijo On sobre los métodos de la clase publicadora para significar que un evento ha ocurrido y que han de ejecutarse las acciones asociadas; de ahí que hayamos decidido llamar al método OnValorCambio (líneas 24-34) de este modo) Obsérvese que en el método EstablecerValor (líneas 36-43), se hace una invocación a OnValorCambio siempre que se pase la prueba lógica valor != numero.

En código cliente (líneas 50-56) se crea una instancia de EjemploEvento (se invoca dentro del constructor al método EstablecerValor y esto hace que se dispare el evento correspondiente), lo mismo ocurre en las líneas 55 y 56.

> Prueba de ejecución.

Resultado:
Evento: El estado del campo `valor` ha cambiado.
Evento: El estado del campo `valor` ha cambiado.
Evento: El estado del campo `valor` ha cambiado.

1.3.2 Modificadores de event

En la Figura 3 se presenta el listado de palabras claves -modificadores- que se pueden aplicar a un tipo de evento:
Palabras claves aplicables a un evento
Figura 3. Palabras claves aplicables a un evento.

2. Funcionamiento y Uso de Eventos (Sección de Extensión)

Los delegados dan origen (o facilitan) al diseño de eventos. Cuando hablamos de delegados y eventos, inmediatamente aparecen dos conceptos nuevos [1]:

  • Broadcaster: Emisor de los eventos. Es sinónimo de clase publicadora.
  • Subscriber: Cliente interesado en los mensajes emitidos por el broadcaster.
A este último se asocian los operadores de suma y resta contextual compuestos: +=, y -=, respectivamente. Con el primero, +=, registramos en el evento de la clase registradora los clientes interesados en escuchar las emisiones del broadcaster. La operación, de disociación con el broadcaster, se lleva a cabo con el operador -=.

2.1 Funcionamiento interno de los eventos

Para comprender el funcionamiento interno de un evento, utilicemos como referencia la siguiente clase publicadora:

public class Broadcaster
{
public event CambioPrecioEventHandler CambioPrecio;
}

La declaración anterior, el compilador la convierte en esta representación (aproximación):

CambioPrecioEventHandler _cambioPrecio; // Delegado privado

public event CambioPrecioEventHandler CambioPrecio
{
add
{
_cambioPrecio += value;
}
remove
{
_cambioPrecio += value;
}
}

Observemos que esta representación es muy parecida a la una propiedad (Propiedades en C#), pero difiere en la especificación de los modificadores de eventos (tema de la tercera parte de esta serie): add, y removeCambioPrecio es la propiedad pública con la que agregamos y removemos clientes de la clase publicadora Broadcaster.

2.2 Ejemplos de uso

Veamos otros ejemplos más para comprender el uso de eventos en el lenguaje de programación C#.

2.2.1 Metrónomo

Archivo de código fuente Metronomo.cs [enlace alternativo]:
En la línea 8 creamos el delegado que registra métodos de clases cliente interesadas en escuchar al broadcaster Metronomo:

public delegate void MarcadorEventHandler(Metronomo m, EventArgs e);

EventArgs como veremos más adelante es el parámetro que soporta la información asociada a un evento y es parte del diseño estándar del patrón de eventos (lo veremos con detalle en la siguiente entrega de esta serie).

En la línea 11 creamos el evento Tic para el registro de subscriptores o clases cliente interesadas en escuchar al evento de cambio del marcador del metrónomo. El método IniciarMetronomo (líneas 16 a 30) se encarga de emitir una marca del metrónomo cada 2000 ms (= 2s) e invocar indirectamente a los métodos asociados al broadcaster con:

Tic(this, e);

Descripción puntual:
  • Tic: identificador del delegado (referencia a la pila de métodos de clases cliente).
  • this: Generador explícito de los eventos (instancia de Metronomo).
  • e: Conjunto de datos asociados al evento.
En las siguientes líneas, específicamente en las líneas 35 a 48, creamos una clase cliente: Subscriptor. Esta clase posee el método de suscripción Subscribir que recibe como argumento un tipo de Metronomo, con el cual realizamos el registro directo del método de escucha:

m.Tic += new Metronomo.MarcadorEventHandler(Accion);

El método Accion (líneas 44-47) es compatible con la firma del delegado MarcadorEventHandler (línea 8).

En el código cliente, creamos una instancia de Metronomo. Esta instancia nos serverá como argumento para la instancia de Subscriptor declarada en la línea 58:

l.Subscribir(m);

La generación de eventos se inicia con la sentencia:

m.IniciarMetronomo();

> Prueba de ejecución.

Resultado:


2.2.2 Divisor

Archivo de código fuente Divisor.cs [enlace alternativo]:
En las línea 10-20 creamos la clase DivisionEventArgs que mantiene la información de la ocurrencia de un evento de división (precisamente el dividendo, el cual se guarda en el campo de sólo lectura numero).

Creamos un clase subscriptora (cliente) llamada SubscriptorDivision en las líneas 25-36. Esta clase es sensible a los cambios de los eventos de división. Presenta los cambios sobre los métodos compatibles con la firma del delegado DivisorEventHandler:
  • MostrarEnConsola: muestra el resultado en la salida estándar.
  • MostrarEnMessageBox: mustra la salida en un componente MessageBox.
En el código cliente, líneas 45-51 se crean instancias del subscriptor SubscriptorDivision, y asociamos los dos métodos anteriores:

SubscriptorDivision dbsl = new SubscriptorDivision();

Divisor7 += new DivisorEventHandler(dbsl.MostrarEnConsola);
Divisor7 += new DivisorEventHandler(dbsl.MostrarEnMessageBox);

GenerarNumeros();

Con GenerarNumeros invocamos de forma indirecta los métodos MostrarEnConsola, y MostrarEnMessageBox.

Resultado:

3. Conclusiones

Hemos aprendido varios de los conceptos esenciales acerca de eventos en C#. Entre ellos sobre las capacidades de emisión de cambios del estado de miembros de una clase (como en el ejemplo del código fuente Divisor.cs). Comprendimos cómo los eventos son convertidos a una representación por parte del compilador análoga a la de una propiedad en C#. En la siguiente entrega vamos a dedicar el tiempo y recursos necesarios para entender el patrón de eventos estándar de Microsoft .NET Framework.

4. Glosario

  • ATM
  • Clase publicadora
  • Delegado
  • Disparar
  • Evento
  • Modificador
  • Subscriber
  • Subscriptor

5. Literatura & Enlaces

[1]: C# 5.0 in a Nutshell by Joseph Albahari and Ben Albahari. Copyright 2012 Joseph Albahari and Ben Albahari, 978-1-449-32010-2.
[2]: EventArgs Class (System) - http://msdn.microsoft.com/en-us/library/vstudio/system.eventargs(v=vs.100).aspx
[3]: The Simplest C# Events Example Imaginable - CodeProject - http://www.codeproject.com/Articles/11541/The-Simplest-C-Events-Example-Imaginable
[4]: EventHandler(TEventArgs) Delegate (System) - http://msdn.microsoft.com/en-us/library/db0etb8x(v=vs.110).aspx
[5]: C# Events - http://www.tutorialspoint.com/csharp/csharp_events.htm
[7]: Delegados en C# - Parte 1: Introducción - http://ortizol.blogspot.com/2013/10/namespaces-en-c-parte-1.html
[8]: event (C# Reference) - http://msdn.microsoft.com/en-us/library/8627sbea.aspx
[9]: Modificadores de Acceso en C# | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/04/modificadores-de-acceso-en-c.html
[10]: Propiedades en C# - http://ortizol.blogspot.com/2013/10/propiedades-en-c.html


J

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.