Está en la página 1de 6

dnm.inicio.

fundamentos
dnm.incio.fundamentos

Guillermo “Guille” Som

Delegados y eventos
Primera parte: ¿En quién delegas tú?

En este número vamos a tratar de los delegados, y también de los eventos (aunque de
estos últimos nos encargaremos con más detalle en el próximo artículo), ya que en
.NET están estrechamente relacionados;tanto es así que no podemos definir un even-
to sin la intervención de un delegado.

<< Delegados y eventos en Visual Basic y C# En este caso, primero se define un delegado y a
continuación hay que definir el evento que debe ser
Seguramente los programadores de Visual Basic del tipo de ese delegado. Complicado, ¿verdad? Ahora
estarán pensando que lo dicho en la entradilla no es mismo no entraremos en muchos detalles sobre esto,
totalmente cierto. Bueno, posiblemente no, porque antes veamos cómo se lanzaría ese evento tanto des-
si están leyendo dotNetManía sabrán lo que se escon- de Visual Basic como desde C#:
de tras los eventos. Pero no está de más aclararlo para
' En Visual Basic:
que no queden dudas. RaiseEvent Click(sender, e)
Lo cierto es que Visual Basic es un lenguaje muy
protector, y nos “libera” de ciertas tareas para facili- // En C#:
tarnos el trabajo real, de forma que nos podamos con- if( Click != null )
{
centrar en lo que de verdad importa y olvidarnos un Click(sender, e);
poco de ciertos “asuntillos” que en parte solo nos }
hacen teclear más.
Aclaremos un poco todo esto que acabo de comen-
tar. En Visual Basic, para definir un evento solo hay que Indudablemente en Visual Basic sigue siendo mucho
escribir la instrucción Event seguida de la definición que más fácil, más simple, menos complicado, no tenemos
daremos al método que recibirá los eventos. Por ejem- que comprobar nada... Y es cierto, a eso es a lo que me
plo, si nuestra clase es del tipo Button, podemos definir refería con lo de que Visual Basic es muy “protector” y
el evento Click de la siguiente forma: nos libera de ciertos detalles que en realidad no nece-
sitamos saber, o al menos, no es obligatorio que sepa-
Public Event Click( ByVal sender As Object,_
ByVal e As EventArgs )
mos. Por otra parte a los programadores de C#, segu-
ramente por aquello de que les gusta “escribir más”
código, pues... ¡que escriban más! Aunque, como vere-
Guillermo “Guille” Som Como podemos comprobar, esa es la definición mos en este artículo, eso ya está cambiando, y ahora
es Microsoft MVP de Visual Basic
desde 1997. Es redactor de
que usaremos en cualquier formulario que quiera podrán hacer también ciertas cosas sin necesidad de
dotNetManía, miembro de Ineta interceptar la pulsación en un botón. Simple, ¿ver- escribir tanto, ya que el propio compilador de C# se
Speakers Bureau Latin America, dad? Veamos ahora cómo tendría que definir ese mis- encargará de algunos aspectos, digamos, de trasfondo.
mentor de Solid Quality Learning mo evento un programador de C#: Pero al final, tanto C# como Visual Basic deben seguir
Iberoamérica y autor del libro
Manual Imprescindible de
las reglas de .NET, y aunque nosotros como usuarios
public delegate void ClickEventHandler(
Visual Basic .NET. object sender, EventArgs e);
no tengamos que preocuparnos, los compiladores sí que
http://www.elguille.info public event ClickEventHandler Click; lo harán.
<< dnm.inicio.fundamentos

Independientemente de las bromas, esto saben mucho los que han desarro- arbitrario, es decir, que no nos “cole-
tenemos que ser conscientes (sobre todo llado con C, incluso los que desarrollan mos” donde no debemos, ya que, como
los programadores de Visual Basic) de con C#. Aunque en todas las puertas fal- ya he dicho, a .NET no le gustan las
que algunas veces el que nos “mimen” sas de .NET siempre hay alguien que sorpresas, por eso impone reglas que
tanto no es bueno, ya que nos acostum- “está por allí” y revisa que en realidad debemos cumplir; si las cumplimos,
bran mal, y cuando creemos que todo no hagamos demasiadas trastadas. Esto pasamos, si no las cumplimos, no nos
va a ser sencillo, llega la versión 2005 y en otros lenguajes no es así, y por error deja seguir.
nos dicen que si queremos usar la nue- o porque así lo hayamos previsto, pode- Y esto es así por todo lo comentado
va instrucción Custom Event debemos mos crear grandes problemas, si no, ¿por anteriormente, ya que el CLR quiere
saber manejar los delegados, además de qué aparecen los fallos de protección seguridad y la única forma de tenerla es
que también debemos saber en qué general? (las típicas pantallas azules de creando normas de conducta y de utili-
medida están relacionados con los even- las versiones anteriores de Windows, zación, en este caso, de la memoria o del
tos. Esto a los programadores de C# no que ahora simplemente están remoza- acceso a esas partes de la memoria en la
les pillaría tan desprevenidos. Así, si aho- das y han cambiado de look por un cua- que están las definiciones de los méto-
ra les dicen que pueden crear métodos dro de diálogo más “mono”). dos o funciones.
anónimos, y que esos métodos anóni-
mos los podrán crear donde se pueda
usar un delegado, o que ya no es nece-
sario usar un constructor para crear un
tipo delegado o que por medio de la
Un delegado permite acceder a una función de
covarianza o la contravarianza podrán
usar de forma más óptima los delega-
forma casi anónima,ya que simplemente tiene la
dos, simplemente estarán preparados y
sabrán soportar el cambio... dirección de memoria de dicha función
Pero como siempre hay gente nue-
va, (tanto en C# como en Visual Basic),
no está de más que algunos puntos estén
totalmente claros, así que eso es lo que Este tipo de problema se debe a un Por tanto, si queremos acceder a un
vamos a intentar en este primer artícu- acceso indebido a la memoria, normal- método, tenemos que hacerlo por medio
lo dedicado a los delegados y a los even- mente causado por un acceso a una posi- de un puntero controlado, y la forma de
tos. Y en los que seguirán, terminare- ción de memoria que no estaba dentro del controlar ese acceso es definiendo un
mos por aclarar casi cualquier duda que rango que teníamos permitido. .NET es prototipo en el que indiquemos de qué
posiblemente se nos pueda presentar a más estricto y menos permisivo para estas tipo es ese método, si recibe paráme-
la hora de trabajar con los eventos y con cuestiones, por tanto, si queremos estar tros, y de hacerlo cuántos y de qué tipo
los delegados. bajo el abrigo de la seguridad de .NET son. Una vez que tenemos definidos
debemos seguir sus normas. todos estos requerimientos, es cuando
¿Qué son y para qué sirven los Como sabemos, .NET define una le decimos al runtime de .NET que nos
serie de tipos de datos, los cuales pode- permita acceder a esa función. De esta
delegados? mos usar indistintamente desde un len- forma, podrá controlar que estamos
Como sabemos, .NET Framework guaje u otro, ya que independientemen- accediendo al sitio correcto.
se caracteriza por ser un entorno de te del nombre que cada compilador le Esa definición del prototipo de fun-
código administrado (managed code) o lo dé, en realidad estamos trabajando con ción (o método) al que queremos acce-
que es lo mismo, a .NET no le gustan los tipos definidos en la librería de cla- der lo hacemos por medio de un delega-
las sorpresas. Si una función tiene que ses, o mejor dicho, en el sistema de tipos do. Podemos pensar que un delegado es
devolver un valor de tipo String, debe comunes (CTS). Y los delegados no son en cierto modo similar a las interfaces (ver
devolver un valor de tipo String; si un una excepción. dotNetManía nº 16), que como sabemos
método debe recibir dos parámetros de Pero... ¿qué es un delegado? Un definen un contrato que debemos respe-
tipo Object, eso es lo que recibirá. Y delegado es una referencia a una fun- tar. Sabiendo esto, si queremos acceder a
todo esto los lenguajes adscritos a .NET ción, lo que también se conoce como un una función que devuelve una cadena y
deben respetarlo, ya que de no ser así, puntero a una función, es decir, un dele- que recibe un parámetro de tipo Cliente,
.NET no permitirá la ejecución del gado permite acceder a una función de debemos definir un delegado con esas
código. Y esto es aplicable a todo lo que forma casi anónima, ya que simplemen- características, y cuando posteriormente
.NET controla, es decir, a todo lo que te tiene la dirección de memoria de queramos acceder a ese método, en lugar
está bajo su influencia. Lo que no quie- dicha función. Y sabiendo la dirección de hacerlo directamente o por medio de
<<dotNetManía

re decir que no podamos hacer cosas que de memoria, podemos acceder a ella. un puntero directo, usaremos un objeto
.NET no permita; pero si lo hacemos, Pero en .NET esto debe estar contro- del tipo definido por el delegado. Lo que
debemos hacerlo por la puerta falsa. De lado, de forma que ese acceso no sea nos lleva a una segunda definición de lo

43
<< dnm.inicio.fundamentos

gado. Veamos el siguiente código y segu-


ro que esa cercanía no es tan necesaria:

Los delegados definen la “firma” que los métodos public static void usarMiFuncionDelegado(
MiFuncionDelegado mfd)
{
a los que queremos acceder deben tener }
string s = mfd( new Cliente() );

En este caso, tenemos un método que


define un parámetro del tipo del delega-
do, por tanto, podemos llamar a ese méto-
que es un delegado, en la que podemos y cómo podemos usarlo. Primero defi- do pasándole la dirección de una función
decir que es un tipo especial que nos per- nimos el delegado usando la misma “fir- que cumpla con la definición del delega-
mite definir la forma de acceder a una ma” que tiene el método al que quere- do y no importará donde estén definidos,
función. mos acceder: ni el método ni la función a la que acce-
Veamos el ejemplo del método que deremos por medio del delega-
devuelve una cadena y recibe un paráme- do. La única condición es que
tro de tipo Cliente. Ese método lo pode- public delegate string MiFuncionDelegado(Cliente c); desde donde hagamos la llama-
mos definir en C# de esta forma: da, tengamos acceso a ambos
Fuente 1. Definición de un delegado para acceder a un
métodos (o funciones), pero no
public string MiFuncion(Cliente c) método definido con la misma firma.
{return “...”;}
tienen porqué estar todas defi-
nidas en una misma clase, ¡ni
Para usar el delegado, definimos una siquiera en un mismo ensamblado!
La forma de usar ese método sería variable de ese tipo, y como los delegados Para acceder a este método lo pode-
algo así: en realidad son como clases, podemos usar mos hacer de esta forma:
el mismo código que usamos para crear
string s = MiFuncion( new Cliente() ); static void Main(string[] args)
cualquier tipo, con la diferencia de que en {
el constructor debemos indicarle la fun- Cliente c = new Cliente();
Por supuesto aquí no estamos usan- ción a la que queremos acceder: usarMiFuncionDelegado(c.MiFuncion);
do ningún puntero, simplemente esta- }
MiFuncionDelegado mfd = new
mos accediendo a ese método de forma MiFuncionDelegado(MiFuncion);
directa. Es más, debido a que no esta- Debemos notar en la forma en que
mos indicando dónde está dicha fun- llamamos al método que recibe el dele-
ción, suponemos que estamos accedien- En este caso, también estamos acce- gado, ya que simplemente le hemos
do desde la propia clase en la que está diendo desde la propia clase al método pasado el nombre de la función. Esto es
definido. Pero si quisiéramos acceder MiFuncion, pero lo dejamos así para man- nuevo en C# 2.0 (aunque no en Visual
de una forma más anónima, podríamos tener las cosas simples. Basic), y la forma en que tendríamos que
definir un delegado que tenga esa defi- Para acceder a esa función por hacerlo en las versiones anteriores sería
nición y solo tendríamos que indicar medio del delegado que acabamos de esta otra:
dónde está definido para permitirnos el crear, lo haremos así:
usarMiFuncionDelegado(new
acceso, sin necesidad de usar una ins- MiFuncionDelegado(c.MiFuncion));
string s = mfd( new Cliente() );
tancia de la clase que lo define.
¿Por qué tanta complicación?
Porque debemos suponer que en cier- Que como podemos comprobar, es Es decir, pasando como argumento
tas circunstancias no tenemos una for- un código muy parecido al usado ante- un objeto creado a partir del “tipo” del
ma directa de acceder a ese método, ya riormente, pero con la diferencia de que delegado.
que si la tuviéramos, no tendríamos en este código no usamos el nombre de De igual forma, el código que mos-
necesidad de complicarnos la vida y usa- la función, sino el del objeto creado a tramos anteriormente en el que usába-
ríamos la forma directa mostrada ante- partir del delegado. mos también un constructor de ese tipo
riormente. Por ejemplo, si queremos Ahora supongamos que este último lo podríamos haber escrito de esta otra
que desde otra parte del código alguien código lo queremos usar en cualquier forma, que como vemos, es más simple
pueda acceder a ese método sin necesi- método de cualquier clase, sin importar e igualmente comprensible.
dad de saber si está definido en una cla- cómo y dónde se haya definido, ¿cómo
se o en otra, podemos usar un delegado podríamos hacerlo? porque para poder
y por medio de dicho delegado acceder definir una variable como en este caso, MiFuncionDelegado mfd = MiFuncion;
<<dotNetManía

al método. deberíamos tener “cercana” la función a


Fuente 2. C# 2.0 permite asignar
Veamos la definición del delegado la que queremos acceder. Y si la tenemos directamente la función sin necesidad
que nos permitiría acceder a esa función cerca, pues no necesitamos usar un dele- de un constructor.

44
<< dnm.inicio.fundamentos

NOTA
En Visual Basic,la forma de acceder a una función siempre es por medio ldftn instance string Cliente::MiFuncion(class Cliente)
de la instrucción AddressOf y la podemos usar también de las dos newobj instance void MiFuncionDelegado::.ctor(object,native int)
formas que acabamos de ver, es decir por medio de un objeto del tipo stloc.0
ldloc.0
del delegado o de forma directa, en cuyo caso, (al igual que ocurre con
newobj instance void Cliente::.ctor()
C# 2.0), será el propio compilador el que determinará si esa función callvirt instance string MiFuncionDelegado::Invoke(class Cliente)
cumple o no los requisitos del parámetro que espera el método: stloc.1
ret
usarMiFuncionDelegado(AddressOf c.MiFuncion)

usarMiFuncionDelegado(New Fuente 4. El código IL es el mismo para las dos formas


MiFuncionDelegado(AddressOf c.MiFuncion)) mostradas en el fuente 3.

En las asignaciones también podemos usar cualquiera de las dos formas:


exclusiva para C#. En En el código del fuente 3 tenemos
Dim mfd As MiFuncionDelegado otro artículo tratare- las dos formas de realizar la asignación
mfd = New MiFuncionDelegado(AddressOf MiFuncion)
mos temas que son y en el fuente 4 vemos el código IL que
Dim mfd As MiFuncionDelegado = AddressOf MiFuncion específicos de Visual crea el compilador, el cual es exactamen-
Basic y por consi- te el mismo en ambos casos.
guiente todo el códi-
En cualquier caso, lo que estamos go será en ese lenguaje, ya que como Métodos anónimos
pasando es la dirección de memoria de comenté anteriormente los delegados son
la función. una pieza clave para los eventos, y debe- Los métodos anónimos son otra de
De todo lo que hemos visto debemos mos saber cómo funcionan los delegados las novedades de C# que están relacio-
concluir que los delegados definen la “fir- para comprender mejor cómo funcionan nadas con los delegados. Y es que en C#
ma” que los métodos a los que queremos los eventos. 2.0 podemos usar una definición de un
acceder deben tener, y que podemos acce- delegado en cualquier sitio que el com-
der a esos métodos por medio de instan- Novedades de C# respecto a pilador esperaría que se asignara un
cias creadas a partir del delegado, y si esta- delegado.
mos usando C# 2.0 el compilador se pue-
los delegados El ejemplo más claro del uso de los
de encargar de averiguar qué delegado es En la versión 2.0 de C# (tal como métodos anónimos es para relacionar un
el que debe usarse y lo usará de forma explicó Octavio Hernández en el núme- evento con un código, pero como aún
transparente para nosotros. ro 20 de esta revista) podemos usar los no hemos tratado con detalle los even-
Antes de ver qué relación tiene todo delegados de forma directa, es decir, sin tos, vamos a verlo con el código que esta-
esto con los eventos, repasaremos algu- necesidad de que tengamos que crear una mos usando últimamente. En el código
nas otras características de los delega- instancia de la clase del delegado al que le del fuente 5 podemos ver cómo crear un
dos que en el fondo también están rela- pasamos como parámetro del construc- método anónimo con la misma firma que
cionadas con los eventos, o en la forma tor la función a la que queremos apuntar, el delegado MiFuncionDelegado, pero al
que finalmente las utilizan los eventos, tal como hemos visto en el código del definirlo nosotros de forma indepen-
pero que no necesariamente usaremos fuente 2. En esos casos, el compilador diente podemos escribir en el cuerpo de
para trabajar con los eventos. comprueba el tipo de la variable que reci- la función anónima lo que creamos con-
be el puntero a la función y si tiene la mis- veniente.
Usos prácticos de los delega- ma firma que la función, será el propio
compilador el que haga el uso adecuado
dos del delegado correspondiente. De hecho, public static void usaMiFuncion4()
Para que nos quede más claro el fun- si examinamos el código IL generado por {
MiFuncionDelegado mfd;
cionamiento de los delegados, vamos a ver el compilador será el mismo en los dos mfd = delegate(Cliente c)
en qué situaciones podemos usarlos. casos. {return “Hola “ + c.Nombre;};
Algunos de los usos que vamos a ver son string s = mfd(new Cliente(“Pepe”));
exclusivos de C# 2.0, y aunque ya se han Console.WriteLine(s);
void usaMiFuncion2(){ }
tratado en otros números de esta revista, MiFuncionDelegado mfd = MiFuncion;
no viene mal darles un repaso. Debido a string s = mfd(new Cliente()); Fuente 5. Definición de un método anónimo.
esa exclusividad de uso en C#, decirle a }
void usaMiFuncion3(){
los lectores que prefieren Visual Basic que MiFuncionDelegado mfd = new
no pasen al siguiente artículo, ya que aún MiFuncionDelegado(MiFuncion); Como es evidente, en este caso no
quedan cosas que explicar que también string s = mfd(new Cliente()); hace falta usar un método anónimo, ya
<<dotNetManía

son válidas para ese lenguaje, aunque } que sería más fácil mostrar directamen-
(como es costumbre en esta sección), el Fuente 3. Las dos formas equivalentes de te el saludo, en lugar de dar tantas vuel-
código mostrado será prácticamente en asignar el puntero a una función. tas, pero lo importante es ver cómo se

45
<< dnm.inicio.fundamentos

pueden usar los métodos anónimos, los cuales tienen Sobre la covarianza, la documentación nos dice
mayor utilidad con los eventos o cuando queramos que: Cuando un método delegado tiene un tipo de valor
simplemente definir la función “apuntada” de forma devuelto que es más derivado que la firma de delegado, se
directa, ya que como hemos visto anteriormente, esa denomina covariante. En realidad, al leerlo sabiendo
variable que apunta al método la podemos pasar como qué es lo que significa no era tan rebuscada la defini-
argumento a otro método. ción. Pero para dejarlo en un lenguaje más llano, dire-
En cualquier caso, lo que no nos estará permitido mos que esto significa que podemos usar delegados
hacer es modificar la firma del delegado; por tanto, si se que devuelvan un tipo y el receptor de ese valor pue-
nos ocurre la brillante idea de añadir un nuevo paráme- de ser cualquier clase de ese mismo tipo o de cualquier
tro a la función, tal como vemos en el siguiente código otra clase derivada.
fuente, el compilador nos avisará (entre otras cosas), que Por ejemplo, en el siguiente código, tenemos la
MiFuncionDelegado no tiene dos argumentos. definición de una clase Persona y un delegado que
devuelve un valor de ese tipo:
mfd = delegate(Cliente c, string saludo){
return saludo + “ “ + c.Nombre; public delegate Persona PersonaCallback();
};
public class Persona
{
Es importante saber que aunque estemos decla- // Omitidas las definiciones
// de las propiedades Nombre y Apellidos
rando un “método” anónimo, en realidad no es un }
método, al menos en el sentido de los ámbitos o
cobertura de las variables, ya que desde ese méto-
do anónimo podemos acceder a cualquier variable Y definimos una clase derivada, a la que llamare-
que hayamos declarado anteriormente, y la varia- mos Colega, en la que definimos un método estático
ble definida en el parámetro también tendrá el mis- que devuelve un objeto de ese mismo tipo:
mo ámbito que el método o propiedad que contie-
public class Colega : Persona
ne esa definición anónima. Debemos pensar en que {
el cuerpo del método anónimo en realidad es como public static Colega NuevoColega()
cualquier otro bloque de código que podamos {
incluir dentro de un par de llaves. return new Colega();
}
En el código del fuente 6 podemos ver ese con- // Omitida la definición de la propiedad Correo
flicto entre la variable definida directamente y la defi-
}
nida como parámetro del método anónimo.
Podemos usar esa función como el método al que
apuntará el delegado PersonaCallback, que como hemos
Cliente c = new Cliente(); visto, devuelve un objeto del tipo Persona:
usarMiFuncionDelegado(c.MiFuncion);
PersonaCallback nColega;
MiFuncionDelegado mfd; nColega = Colega.NuevoColega;
mfd = delegate(Cliente c) {return “Hola “ + c.Nombre;};
string s = mfd(new Cliente(“Manolo”)); Colega unColega = (Colega)nColega();
Console.WriteLine(s); // Omitidas las asignaciones a las propiedades

Fuente 6. El ámbito de un método anónimo es el mismo que


En la asignación a la variable unColega debemos
el del bloque en el que se define.
hacer una conversión (cast) ya que el valor devuelto
por el delegado es del tipo Persona, independiente-
mente del tipo que devuelva en realidad la función a
Covarianza y contravarianza
la que hace referencia ese delegado.
La primera vez que leí estas dos palabras, pensé En las versiones anteriores, para conseguir esto
que el tema tratado debía ser muy complicado, segu- mismo teníamos que definir un delegado para cada
ramente para gente más experta que yo en C#. Y como una de los tipos que quisiéramos devolver. Ni que decir
soy un catetillo de pueblo que no tiene estudios, des- tiene que esto solo lo podemos hacer con C#; en Visual
pués de leer la descripción de la ayuda de Visual Studio Basic no está permitida esta forma de usar los valores
2005 pensé en la suerte que tenía de que mi lenguaje devueltos por un delegado.
materno no fuese el C#. Pero si iba a hablar de esto Veamos ahora qué nos dice la documentación sobre
en este artículo, lo lógico era que me empapara del la contravarianza: Cuando una firma de método delegado
<<dotNetManía

tema, al menos si lo iba a tratar. Y con esto pasa como tiene uno o más parámetros de tipos que derivan de los tipos
con casi todo, hasta que no lo tienes entre las manos, de los parámetros de método, ese método se denomina con-
no sabes el tacto que tiene. travariante. Lo que viene a significar que los tipos de

46
<< dnm.inicio.fundamentos

datos que podemos usar como parámetros al llamar a pero siempre y cuando el método anónimo tenga la
un delegado pueden ser del mismo tipo que está defi- firma correcta, es decir, el parámetro en la definición
nido en el delegado (así era hasta la versión 2.0), o de del método anónimo debe ser del mismo tipo que el
cualquier tipo derivado. Si el compilador ve una rela- indicado en la definición del delegado, pero a la hora
ción de herencia entre el tipo usado y el definido en de usarlo podemos indicar cualquier objeto de un tipo
el delegado, lo permitirá. Cliente o derivado:
Siguiendo con el ejemplo del delegado que recibe
MiFuncionDelegado mfd3;
un parámetro de tipo Cliente, (ver el fuente 1), vamos a mfd3 = delegate(Cliente co2) {return “A sus pies “ +
rediseñar la clase y el método MiFuncion para que devuel- co2.Nombre;};
va un saludo al nombre indicado en la propiedad Nombre Console.WriteLine(mfd3(co));
de la clase. A continuación creamos la clase ClienteOro
que se deriva de Cliente. Tanto en una como en otra cla- Pero el uso más práctico de esta característica será
se hemos definido un constructor que recibe como pará- (como casi todo lo relacionado con los delegados),
metro el nombre a usar. En el fuente 7 vemos esas dos cuando lo apliquemos a los eventos.
definiciones de estas clases. Cuando utilizamos los métodos de eventos de los
controles de Windows Forms, el segundo parámetro
suele (o debería) ser una clase derivada de EventArgs,
public class Cliente
{
pero dependiendo del evento, ese parámetro será del
// Omitida la definición de la propiedad Nombre tipo adecuado para el evento en cuestión. Por ejem-
public Cliente() { } plo, el evento KeyPress recibe un parámetro del tipo
public Cliente(string nombre) KeyPressEventArgs, pero en C# podemos definir el
{ this.Nombre = nombre; }
//
método que intercepta ese evento de cualquiera de
public virtual string MiFuncion(Cliente c) { estas dos formas:
return “Que tal “ + c.Nombre;
} private void txtNombre_KeyPress( object sender,
} KeyPressEventArgs e)
{ ... }
class ClienteOro : Cliente
{ private void txtNombre_KeyPress( object sender,
public ClienteOro() { } EventArgs e)
public ClienteOro(string nombre) { ... }
{ this.Nombre = nombre; }
}
La segunda forma, a pesar de ser menos espe-
Fuente 7. Definición simplificada de las clases cífica, seguramente la usaremos en casos muy con-
Cliente y ClienteOro.
cretos y siempre que necesitemos esa “generalidad”,
pero no adelantemos acontecimientos, ya que cuan-
NOTA do tratemos el tema de los eventos veremos algu-
El hecho de definir un constructor con parámetro en las cla- nas aplicaciones prácticas de esta posibilidad que
ses del fuente 7 es para facilitar el uso de las mismas, de for- tiene C#, ya que en Visual Basic siempre tendre-
ma que en una sola instrucción podamos asignar el valor de mos que definir los parámetros de los eventos del
la propiedad Nombre, de esa forma nuestro código de ejem- tipo exacto.
plo podrá mostrar algo. Pero la razón de que lo hayamos teni-
do que hacer en las dos clases es porque los constructores
no se heredan; por tanto, si queremos esa funcionalidad en Conclusiones
las dos clases, debemos definirlos en ambas.
Aún no hemos terminado con algunas de las
cosas interesantes o importantes de los delegados,
Ahora podemos usar cualquier función que reci- pero será en el próximo artículo donde veremos
ba un delegado que apunte a MiFuncion y el compila- otra característica interesante de los delegados: la
dor (realmente el runtime) usará la clase que corres- multidifusión. Esa forma de usar los delegados la
ponda. comprenderemos mejor cuando sepamos más sobre
la estrecha relación de estas clases especiales con
ClienteOro co = new ClienteOro(“Paco”);
MiFuncionDelegado mfd2;
los eventos.
mfd2 = co.MiFuncion; Como es costumbre en esta sección, en el sitio
string sco = mfd2(co); Web de dotNetManía está disponible el código de
Console.WriteLine(sco); ejemplo para poder bajarlo, aunque en el caso de Visual
<<dotNetManía

Basic no será equivalente al de C#, simplemente por-


Incluso podemos crear un método anónimo que que aquél no soporta algunas de las características de
use un objeto del tipo ClienteOro como parámetro, los delegados que hemos tratado.

47

También podría gustarte