Hola, ¿qué tal?
Hace ya tiempo que he publicado los webcasts sobre el tema de delegados, sin embargo, quisiera resaltar la importancia de estos desde el punto de vista de la comunicación interna de las aplicaciones, en sí, resaltar la importancia de los eventos en nuestras aplicaciones y de cómo y en qué situaciones son útiles. Me extenderé un poco con hasta el uso de eventos genéricos, como parte recomendable del uso de eventos.
Pues bien, primeramente debemos entender lo que es un evento. Un evento es parte de la funcionalidad que debe presentar un objeto, aunque no es obligatoria y muchas veces hasta es ignorada. Es curios ver que la programación utilizando el .NET Framework está orientada a eventos y que en nuestros desarrollos terminamos olvidando en ocasiones crear la funcionalidad necesaria para construir objetos que expongan eventos para estar acordes a la práctica de programación por eventos. Los eventos de cierta manera es la manera en que “sienten” nuestros objetos, por compararlo con un ser vivo, ya que muchos seres vivos reaccionan a los sentimientos que produce su entorno, no es raro ver que si un perro se siente agredido pues ataque, o bien, que escape, o también, como nosotros, sentimos hambre y comemos, sentimos frio y nos abrigamos, y cosas por el estilo. También podría comparar a un evento con algo parecido a cada acción corresponde una reacción. Así pues podríamos decir que la acción es lo que puede realizar un objeto, típicamente en un método y la reacción podría ser un evento, viéndolo así, el evento es la capacidad de reaccionar de nuestros objetos a ciertas acciones.
Nuestras clases, (porque formularios, páginas, controles, etc. Son clases) deben reaccionar a ciertas acciones, comúnmente exponemos funcionalidad sin considerar la reacción que podría tener para extender la funcionalidad, por ejemplo, creamos un método salvar y no pensamos que nuestro objeto podría reaccionar a esto con un evento denominado ElementoSalvado, o RegistroSalvado o ItemSalvado, de esta manera podríamos tener el retorno de información sucedida en la acción de Salvar, por ejemplo, si se salvó o no, qué se salvo y hasta el retorno de un objeto construido a partir de la información que se utilizó para salvar. En este evento podríamos tomar decisiones de acuerdo a la información retornada con mucha mayor funcionalidad.
Los eventos son útiles cuando hablamos de uso de objetos de componentes dentro de otros objetos, como utilizar un objeto personalizado dentro de un formulario. Es bueno tener una manera de manejar lo que está pasando al realizar una acción que requiera la atención de la aplicación, con esto quiero decir que no todas las acciones están obligadas a tener un evento, pero sí todas las que tengan ciertos procesos que merezcan la atención de la aplicación.
Debemos tener algunas consideraciones de diseño, esto es, debemos identificar al necesidad real de incluir un evento en nuestras clases, y esto es porque aunque son altamente funcionales en ocasiones no son necesarios por la naturaleza de la acción.
Para identificar básicamente la necesidad de un evento, debemos responder a las siguientes preguntas:
¿La acción que se va a ejecutar genera información que será necesaria para otros procesos o para desencadenar otras acciones?
¿La acción que se va a ejecutar influye en otras partes de la aplicación y es necesaria para actualizar la información presentada o modificar el estado de la aplicación?
¿La acción que se va a ejecutar proveerá información para desplegar resultados o información en otras partes de la interfaz de usuario?
¿La acción que se va a ejecutar proveerá información que influirá en el flujo de la ejecución de la aplicación?
A grandes rasgos se puede tener en mente estás pregunta, sin embargo, puede existir un evento si existen requerimientos particulares para la funcionalidad de la aplicación dependiendo de las acción es que se ejecuten.
Los ejemplos más notorios son los eventos de los controles que utilizamos día a día en nuestros desarrollos, un evento típico es el que se desata al dar click en un botón, este evento es una manera de reaccionar a una acción del usuario, sin embargo, podemos tener eventos que reacciones a acciones propias de la aplicación y esos son los que nos ocupan en esta ocasión.
Bueno, veamos esto con un ejemplo. Consideremos una acción que modificará el estado de un formulario, esto es, tenemos un método que realiza un cálculo y en base al resultado del cálculo será que mostremos un indicador con un color en nuestro formulario, pensaremos que todo esto se realiza en un objeto definido por una clase de nuestra autoría.
Veamos entonces…
Primeramente definamos un delegado, el cual será ocupado para ligar eventos a métodos que del lado de la ejecución. La declaración de delegados es simple, sin embargo debemos tener siempre presente que un delegado es un tipo, tal como lo es una clase o un estruct, hago la aclaración para tener en cuenta que la definición de un delegado es la definición de un tipo, con este tipo entonces podremos declarar variables del tipo del delegado que creamos, esto es, las variables que declaremos utilizarían como tipo de dato el delegado declarado. Vemos entonces cómo declaramos un delegado:
public delegate void CalculoTerminadoEventHandler(object sender, EventArgs e);
Esta será la firma que deberá cumplir el método que se utilice para manejar el evento, aquí hay dos puntos interesantes, el primero es que se podrá generar una clase personalizada derivada de EventArgs que contenga información útil para utilizar el manejador de evento, el otro punto es que “sender” puede ser de el tipo específico de nuestra clase, esto se logra haciendo la declaración de un delegado genérico como se muestra a continuación:
public delegate void CalculoTerminadoEventHandler<T,U>(T sender, U e);
La declaración es similar, solo que en este caso estamos utilizando parámetros de tipos para definir los parámetros de la firma del delegado, más adelante mostraré cómo es que se crea la declaración utilizando este delegado genérico. Nótese que se utiliza la terminación EventHandler, lo cual es para indicar que este delegado será utilizado en la declaración de eventos, esta terminación se utiliza por convención al igual que la firma del delegado.
Creemos ahora una clase de para los argumentos del evento, la cual será útil para obtener información de la acción, está clase debe derivarse, o bien, ser heredada de EventArgs.
La clase que crearemos será la siguiente:
public class CalculoEventArgs : EventArgs
{
private string operadorUtilizado;
private int cantidadOperandos;
private bool operacionExitosa;
public CalculoEventArgs(): base()
{
}
public string OperadorUtilizado
{
get
{
return operadorUtilizado;
}
set
{
operadorUtilizado = value;
}
}
public string CantidadOperandos
{
get
{
return cantidadOperandos;
}
set
{
cantidadOperandos = value;
}
}
public bool OperacionExitosa
{
get
{
return operacionExitosa;
}
set
{
operacionExitosa = value;
}
}
}
Para utilizar esta clase en nuestra firma de delegado, solo debemos cambiar la declaración anterior del delegado por esta nueva:
public delegate void CalculoTerminadoEventHandler(
object sender, CalculoEventArgs e);
En el caso del delegado genérico no hay necesidad de cambiar nada, solo tenemos que utilizarlo y listo. Veremos primeramente cómo utilizar los eventos de la manera típica, y lo que haremos será lo siguiente, primera mente construir una clase llamada Cálculo, en la cual realizaremos algunas operaciones básicas para ejemplificar los eventos. Así pues, aquí está nuestra clase de ejemplo:
public class Calculo
{
public event CalculoTerminadoEventHandler CalculoTerminado;
public Calculo()
{
}
public int CalcularSuma(List<int> operandos)
{
int res = 0;
CalculoEventArgs args = new CalculoEventArgs();
args.CantidadOperandos = operandos.Count;
args.OperadorUtilizado = "SUMA";
try
{
foreach (int op in operandos)
{
res += op;
}
args.OperacionExitosa = true;
}
catch
{
res = 0;
args.OperacionExitosa = false;
}
CalculoTerminado(this, args);
return res;
}
public int CalcularResta(List<int> operandos)
{
int res = 0;
CalculoEventArgs args = new CalculoEventArgs();
args.CantidadOperandos = operandos.Count;
args.OperadorUtilizado = "RESTA";
try
{
foreach (int op in operandos)
{
res -= op;
}
args.OperacionExitosa = true;
}
catch
{
res = 0;
args.OperacionExitosa = false;
}
CalculoTerminado(this, args);
return res;
}
public int CalcularMultiplicacion(List<int> operandos)
{
int res = 0;
CalculoEventArgs args = new CalculoEventArgs();
args.CantidadOperandos = operandos.Count;
args.OperadorUtilizado = "Multiplicacion";
try
{
foreach (int op in operandos)
{
res += op;
}
args.OperacionExitosa = true;
}
catch
{
res = 0;
args.OperacionExitosa = false;
}
CalculoTerminado(this, args);
return res;
}
Para concluir solo nos falta utilizar nuestra clase en un programa, así que en una aplicación de consola de ejemplo podríamos utilizarla así:
class Program
{
static void Main(string[] args)
{
Calculo calculo = new Calculo();
calculo.CalculoTerminado +=
new CalculoTerminadoEventHandler(calculo_CalculoTerminado);
List<int> operandos = new List<int>();
operandos.Add(1);
operandos.Add(1);
operandos.Add(1);
operandos.Add(1);
operandos.Add(1);
Console.Write("El Resultado: {0} \n\n",
calculo.CalcularSuma(operandos).ToString());
Console.Write("El Resultado: {0} \n\n",
calculo.CalcularResta(operandos).ToString());
Console.Write("El Resultado: {0} \n\n",
calculo.CalcularMultiplicacion(operandos).ToString());
Console.ReadLine();
}
static void calculo_CalculoTerminado(object sender, CalculoEventArgs e)
{
if (e.OperacionExitosa)
Console.Write(
"Se calculó con {0} operandos, la operación fue {1} y fue exitosa\n",
e.CantidadOperandos.ToString(), e.OperadorUtilizado);
else
Console.Write(
"Se calculó con {0} operandos, la operación fue {1} y falló\n",
e.CantidadOperandos.ToString(), e.OperadorUtilizado);
}
}
Cabe hacer notar la manera en que agregamos el método manejador del evento al evento utilizando el operador +=, además de que podríamos agregar más métodos manejadores dependiendo de nuestras necesidades, también se pueden desasociar los métodos agregados al evento con el operador -=.
Bueno, con esto concluyo este tema, espero que sea de utilidad y que haya servido para mostrar que no es tan difícil utilizar un evento y que además nos da ventajas a proporcionarnos más información que el simple resultado de un método o que la ejecución de una tarea.
En una publicación siguiente les mostraré como utilizar el delegado genérico para uso de eventos genéricos. si quieren saber más acerca de los delegados no dejen de darse una vuelta a mi página de WebCast, donde encontrarán la presentación de delegados y el screencast sobre un ejemplo del uso de delegados para hacer eventos.
Saludos…
Octavio Telis