Guillermo “Guille” Som
dnm.inicio.fundamentos
dnm.incio.taller
Parámetros personalizados en los eventos
En este cuarto artículo dedicado a los delegados y eventos nos centraremos en cómo
comunicarnos entre la clase que define los eventos y la que los intercepta.Veremos esa
comunicación de dos formas diferentes,usando parámetros por referencia y de la for-
ma recomendada, que es definiendo nuestra propia clase para usar como parámetro
de los eventos.También abordaremos una característica exclusiva de C#,que es la posi-
bilidad de usar clases base como parámetros de los métodos que reciben los eventos.
<<
Comunicarse con la clase que produce el evento
Hasta ahora hemos estado usando los eventos de
la forma habitual: para comunicarle a la aplicación
receptora que algo ha ocurrido, y en esa comunica-
ción solo hemos usado una vía, de forma que la cla-
se que produce el evento “informe” de qué es lo que
está ocurriendo, información que enviamos con los
parámetros pasados al método receptor. Pero, ¿cómo
podemos hacer que la aplicación receptora (el clien-
te) pueda comunicarse con la clase que produce el
evento (el servidor), por ejemplo, para cancelar una
acción o para pasarle cierta información?
Usando parámetros por referencia en los eventos
En principio, esto es algo sencillo de realizar, ya
que lo único que tenemos que hacer es declarar por
referencia uno (o varios) de los parámetros. De esta
forma podemos asignar un valor a ese parámetro y
usarlo en la clase que produce el evento. Por ejem-
plo, si tenemos el evento usado en los ejemplos de
los artículos anteriores que avisa del cambio en el
valor de una propiedad, podemos agregar un tercer
parámetro para indicar si cancelamos dicha asigna-
ción. En el código del fuente 1 vemos cómo definir
el delegado, el evento y la propiedad, que tiene en
cuenta esta nueva posibilidad de cancelar la asigna-
ción, dato que sabremos si la aplicación cliente
devuelve un valor verdadero en la variable usada para
el tercer parámetro.
Como es de esperar, el método de la clase clien-
te que recibe la notificación de que el evento se ha
producido debe tener la misma “firma” que el dele-
Guillermo “Guille” Som
Es Microsoft MVP de Visual Basic
desde 1997.Es redactor de
dotNetManía,mentor de Solid
Quality Iberoamericana,tutor de
campusMVP,miembro de Ineta
Speakers Bureau Latin
America,y autor de
los libros “Manual Imprescindible
de Visual Basic .NET” y
“Visual Basic 2005”.
http://www.elguille.info
public delegate void NombreCambiadoEventHandler(
string nuevo, string anterior, ref bool cancelar);
public event NombreCambiadoEventHandler NombreCambiadoRef;
private string m_Nombre;
public string Nombre
{
get { return m_Nombre; }
set
{
// Lanzar el evento
// indicando el nuevo valor y el anterior,
// además de la variable para saber si cancela
if( NombreCambiadoRef != null )
{
bool cancelado = false;
NombreCambiadoRef(value, m_Nombre, ref cancelado);
if( cancelado )
{
// Lo que haya que hacer para avisar
// que se ha cancelado la asignación
return;
}
}
m_Nombre = value;
}
}
Fuente 1. Definición de un evento que permite cancelar la acción
gado, incluyendo el parámetro por referencia y, como
es natural, podemos ignorar dicho parámetro o asig-
narle un valor verdadero en el caso de que queramos
cancelar la asignación. En el código del fuente 2 can-
celamos esa asignación si el nuevo valor es una cade-
na vacía o la longitud de la misma es inferior a 3
caracteres.
a
í
n
a
M
t
e
N
t
o
d
<
<
35
<<
dnm.inicio.taller
a
í
n
a
M
t
e
N
t
o
d
<
<
36
static void cli_NombreCambiadoRef(
string nuevo, string anterior, ref bool cancelar)
{
// Cancelamos la acción si el nuevo valor no
// cumple las condiciones que estimemos oportunas
if( string.IsNullOrEmpty(nuevo) || nuevo.Length < 3 )
{
Console.WriteLine( “cancelando... debe tener más” +
“ de 2 caracteres...”);
cancelar = true;
}
}
Fuente 2. El método que recibe el evento puede
cancelar la asignación
Hay que aclarar que esa comprobación que hace-
mos en el lado del cliente en realidad la podíamos
hacer en la propiedad, pero de lo que aquí se trata es
de ver un ejemplo de cómo cancelar (o informar a la
clase que ésa es nuestra intención), no de la solución
mágica a todos nuestros problemas. Además, al hacer-
lo en el lado del cliente, esto nos da la posibilidad de
que cada aplicación que use esa clase decida la com-
probación que debe hacer. Por ejemplo, si la propie-
dad fuera una contraseña, el cliente podría validar si
cumple ciertos requisitos impuestos por la aplicación.
Usando clases como parámetro de los eventos
Si necesitamos pasar más datos (ya sean por valor
o por referencia), tendremos que indicar más pará-
metros en la definición del delegado asociado con el
evento, y aunque eso no es ningún problema, .NET
nos permite una forma más elegante de hacerlo: usan-
do un tipo personalizado como parámetro, es decir,
una clase. Es más, el propio .NET Framework tiene
una clase que podemos usar para estos menesteres:
EventArgs. De hecho, todos los eventos de los contro-
les del espacio de nombres Windows.Forms se basan (o
se derivan) de esa clase; por tanto, podemos hacer lo
mismo que hace el entorno en el que estamos progra-
mando, y crear nuestra propia clase basada en
EventArgs. De esa forma también nos resultará más
cómodo pasar (y recuperar) datos en ambos sentidos,
con lo que de camino nos ahorramos la definición de
parámetros por referencia, que entre otras cosas, no
son deseables, al menos a la hora de dar claridad a
nuestro código.
Crear clases para usar como parámetro
de un evento
Para mantener las cosas sencillas, vamos a crear
una clase que usaremos como parámetro de los even-
tos. Esta clase tendrá la misma funcionalidad que la
mostrada en el código del fuente 1; es decir, la clase
tendrá tres propiedades, dos de solo lectura y la ter-
cera de lectura/escritura, ya que será la usada para
saber si se quiere cancelar la acción de asignar un nue-
vo valor a la propiedad. Las otras dos, las que indican
los valores nuevo y anterior, serán de solo lectura, ya
que lo único que le interesa al método receptor del
evento es saber qué valores contienen, y como son
valores directamente relacionados con una propiedad
de una clase, no deberíamos manipularlos, ya que si
lo hacemos nos estaríamos saltando a la torera uno de
los pilares de la programación orientada a objetos: la
encapsulación. Pero... como somos los diseñadores de
la clase, podemos hacer lo que mejor nos parezca, aun-
que no voy a ser yo el que incite a esa decisión.
Las dos propiedades que vamos a exponer como
de solo lectura las vamos a definir con los dos bloques
de código que habitualmente tienen todas las propie-
dades, que como sabemos son el bloque get, que nos
permite obtener el valor de la propiedad, y el bloque
set, que es el usado cuando asignamos el valor. Para
dar esa funcionalidad de solo lectura a pesar de defi-
nir el bloque set, vamos a usar la nueva característi-
ca de .NET Framework 2.0 que nos permite cambiar
el ámbito (o visibilidad) de uno de los bloques de las
propiedades (ver el número 20 de dotNetManía). De
esta forma, la clase expondrá públicamente solo el blo-
que get, y el bloque set lo dejaremos como internal
(Friend en Visual Basic) para que solo podamos usar-
lo desde el propio ensamblado.
En el código del fuente 3 vemos la definición de
la clase DatosCambiadosEventArgs, que es la que usa-
remos como parámetro del delegado y el evento
definidos en la clase Cliente (ver el fuente 4). Ambas
clases están definidas en una biblioteca de clases,
que tendremos que añadir a las referencias del pro-
public class DatosCambiadosEventArgs : EventArgs
{
// Propiedad de solo lectura cuando se accede
// desde fuera del propio ensamblado
private string m_Nuevo;
public string Nuevo
{
get { return m_Nuevo; }
internal set { m_Nuevo = value; }
}
// Propiedad de solo lectura cuando se accede
// desde fuera del propio ensamblado
private string m_Anterior;
public string Anterior
{
get { return m_Anterior; }
internal set { m_Anterior = value; }
}
private bool m_Cancelar;
public bool Cancelar
{
get { return m_Cancelar; }
set { m_Cancelar = value; }
}
}
Fuente 3. La definición de la clase basada en EventArgs
<<
dnm.inicio.taller
public class Cliente
{
public delegate void DatosCambiadosEventHandler(
DatosCambiadosEventArgs e);
public event DatosCambiadosEventHandler NombreCambiado;
private string m_Nombre;
public string Nombre
{
get { return m_Nombre; }
set
{
// Lanzar el evento
// indicando el nuevo valor y el anterior
if( NombreCambiado != null )
{
// Declaramos un objeto del tipo de la clase
// usada para usar como parámetro del evento.
DatosCambiadosEventArgs e =
new DatosCambiadosEventArgs();
e.Cancelar = false;
// Desde el propio ensamblado podemos acceder
// al bloque set de las propiedadades Nuevo
// y Anterior
e.Nuevo = value;
e.Anterior = m_Nombre;
// Lanzamos el evento usando como argumento el
// objeto creado a partir de la clase.
NombreCambiado(e);
// Comprobar si se cancela la asignación
if( e.Cancelar )
{
// Lo que haya que hacer para avisar
// que se ha cancelado la asignación
return;
}
}
m_Nombre = value;
}
yecto en el que definimos la clase que interceptará
los eventos y añadir la importación correspondien-
te del espacio de nombres que las contiene. Una vez
hecho eso, la podremos usar tal como vemos en el
código fuente 5.
Definir la clase del evento en el mismo ensamblado
que el cliente
Si en lugar de crear un ensamblado para las clases
(la que produce el evento y la que define el paráme-
tro de dicho evento) decidimos que estén todas en el
mismo ensamblado (proyecto), el hecho de propor-
cionar ámbitos diferentes a los bloques set de las dos
propiedades que queremos que sean de solo lectura
no nos soluciona la papeleta, ya que al estar declara-
dos esos bloques con internal, serán accesibles desde
cualquier parte del mismo ensamblado, y por tanto
también desde el código de la aplica
Comentarios de: Parámetros personalizados en los eventos (0)
No hay comentarios