You are on page 1of 70

Contenido

Delegados (Gua de programacin de C#) ...................................................................................................... 3 Delegate (Clase) ........................................................................................................................................................................ 4 Utilizar delegados (Gua de programacin de C#) .................................................................................................................... 7 Cundo se utilizan delegados en lugar de interfaces (Gua de programacin de C#) ............................................................ 10 Delegados con mtodos con nombre y delegados con mtodos annimos (Gua de programacin de C#) ......................... 10 Mtodos annimos (Gua de programacin de C#) ................................................................................................................ 12 Covarianza y contravarianza en los delegados (Gua de programacin de C#) ...................................................................... 14 Cmo: Combinar delegados (delegados de multidifusin) (Gua de programacin de C#).................................................... 16 Cmo: Declarar un delegado, crear instancias del mismo y utilizarlo (Gua de programacin de C#) ................................... 17 EventHandler (Delegado) ........................................................................................................................................................ 21 EventArgs (Clase)..................................................................................................................................................................... 23 Eventos (Gua de programacin de C#) ......................................................................................................... 25 Cmo: Suscribir y cancelar la suscripcin a eventos (Gua de programacin de C#).............................................................. 26 Cmo: Publicar eventos que cumplan las directrices de .NET Framework (Gua de programacin de C#) ........................... 29 Cmo: Producir eventos de una clase base en clases derivadas (Gua de programacin de C#) ........................................... 32 Cmo: Implementar eventos de interfaz (Gua de programacin de C#) ............................................................................... 35 Sincronizacin de subprocesos (Gua de programacin de C#) .............................................................................................. 39 Cmo: Utilizar un diccionario para almacenar instancias de eventos (Gua de programacin de C#) ................................... 43 Cmo: Implementar descriptores de acceso de eventos personalizados (Gua de programacin de C#) ............................. 45 Ejemplo de eventos ................................................................................................................................................................. 45 Diseo de eventos ................................................................................................................................................................... 47 Controlar y provocar eventos................................................................................................................................ 48 Eventos y delegados ................................................................................................................................................................ 49 Cmo: Conectar mtodos controlador de eventos a eventos ................................................................................................ 51 Para agregar un mtodo controlador de eventos para un evento ................................................................................. 51 Utilizar eventos ....................................................................................................................................................................... 51 Cmo: Consumir eventos en una aplicacin de formularios Web Forms ............................................................................... 55 Para controlar un evento de clic de botn en una pgina Web ..................................................................................... 55 Cmo: Consumir eventos en una aplicacin de formularios Windows Forms ....................................................................... 57 Para controlar un evento de clic de un botn en un formulario Windows Forms ......................................................... 57 Provocar un evento ................................................................................................................................................................. 59

Cmo: Implementar eventos en una clase ............................................................................................................................. 60 Para implementar un evento sin datos especficos del evento ...................................................................................... 60 Para implementar un evento con datos especficos del evento ..................................................................................... 61 Cmo: Provocar y utilizar eventos .......................................................................................................................................... 62 Provocar mltiples eventos..................................................................................................................................................... 66 Cmo: Controlar varios eventos mediante propiedades de eventos ..................................................................................... 67 Para controlar varios eventos mediante las propiedades de evento ............................................................................. 67 Cmo: Declarar eventos que evitan que se pierda memoria ................................................................................................. 68 Cmo: Controlar varios eventos mediante propiedades de eventos ..................................................................................... 69 Para controlar varios eventos mediante las propiedades de evento ............................................................................. 69

Delegados (Gua de programacin de C#) Un delegado es un tipo que define una firma de mtodo y se puede asociar a cualquier mtodo con una firma compatible. Puede invocar (o llamar) al mtodo a travs del delegado. Los delegados se utilizan para pasar mtodos como argumentos a otros mtodos. Los controladores de eventos no son ms que mtodos que se invocan a travs de delegados. Cree un mtodo personalizado y una clase como un control de Windows podr llamar al mtodo cuando se produzca un determinado evento. En el siguiente ejemplo se muestra la declaracin de un delegado: public delegate int PerformCalculation(int x, int y);

Cualquier mtodo de cualquier clase o estructura accesible que coincida con la firma del delegado, la cual est compuesta por los parmetros y el tipo de valor devuelto, puede asignarse al delegado. El mtodo puede ser un mtodo esttico o un mtodo de instancia. Esto permite el cambio mediante programacin de las llamadas a mtodos y la incorporacin de nuevo cdigo en las clases existentes. Si conoce la firma del delegado, puede asignar su propio mtodo. Nota: En el contexto de la sobrecarga de mtodos, la firma de un mtodo no incluye el valor devuelto. Sin embargo, en el contexto de los delegados, la firma s incluye el valor devuelto. En otras palabras, un mtodo debe tener el mismo valor devuelto que el delegado.

Esta capacidad para hacer referencia a un mtodo como parmetro hace que los delegados sean idneos para definir mtodos de devolucin de llamada. Por ejemplo, un algoritmo de ordenacin se podra pasar como referencia al mtodo que compara dos objetos. La separacin del cdigo de comparacin permite programar el algoritmo de forma ms general. Informacin general sobre delegados

Los delegados tienen las propiedades siguientes: Los delegados son similares a los punteros a funcin de C++, pero poseen seguridad de tipos. Los delegados permiten pasar los mtodos como parmetros. Los delegados pueden utilizarse para definir mtodos de devolucin de llamada. Los delegados pueden encadenarse; por ejemplo, se puede llamar a varios mtodos en un solo evento. No es necesario que los mtodos coincidan exactamente con la firma de delegado. Para obtener ms informacin, vea Covariance and ContravarianceCovarianza y contravarianza en los delegados (Gua de programacin de C#). La versin 2.0 de C# introdujo el concepto de mtodos annimos, los cuales permiten pasar bloques de cdigo como parmetros en lugar de utilizar mtodos definidos independientemente. C# 3.0 introdujo las expresiones lambda como una manera ms concisa de escribir bloques de cdigos insertados. Tanto los mtodos annimos como las expresiones lambda (en ciertos contextos) se compilan como tipos delegados. En conjunto, estas caractersticas se conocen ahora como funciones annimas. Para obtener ms informacin sobre las expresiones lambda, vea Funciones annimas (Gua de programacin de C#).

En esta seccin

Utilizar delegados (Gua de programacin de C#) Cundo se utilizan delegados en lugar de interfaces (Gua de programacin de C#) Delegados con mtodos con nombre y delegados con mtodos annimos (Gua de programacin de C#) Mtodos annimos (Gua de programacin de C#) Covarianza y contravarianza en los delegados (Gua de programacin de C#) Cmo: Combinar delegados (delegados de multidifusin) (Gua de programacin de C#) Cmo: Declarar un delegado, crear instancias del mismo y utilizarlo (Gua de programacin de C#)

Delegate (Clase) Representa un delegado, que es una estructura de datos que hace referencia a un mtodo esttico o a una instancia de clase y a un mtodo de instancia de dicha clase. Espacio de nombres: System Ensamblado: mscorlib (en mscorlib.dll) Sintaxis [SerializableAttribute] [ClassInterfaceAttribute(ClassInterfaceType.AutoDual)] [ComVisibleAttribute(true)] public abstract class Delegate : ICloneable, ISerializable Comentarios

La clase Delegate es la clase base para los tipos de delegado. No obstante, el sistema y los compiladores son los nicos que pueden derivar explcitamente a partir de la clase Delegate o de la clase MulticastDelegate. Tampoco est permitido derivar un tipo nuevo a partir de un tipo de delegado. La clase Delegate no se considera un tipo de delegado; es una clase que se utiliza para derivar tipos de delegado. La mayora de los lenguajes implementan una palabra clave delegate y los compiladores de dichos lenguajes pueden derivar a partir de la clase MulticastDelegate; por lo tanto, los usuarios deben emplear la palabra clave delegate que proporciona el lenguaje. Adems de los mtodos heredados, Common Language Runtime proporciona dos mtodos especiales para los tipos de delegado: BeginInvoke y EndInvoke. Para obtener ms informacin sobre estos mtodos, vea Llamar a mtodos sincrnicos de forma asincrnica. La declaracin de un tipo de delegado establece un contrato que especifica la firma de uno o varios mtodos. Un delegado es una instancia de un tipo de delegado que contiene referencias a: Un mtodo de instancia de un tipo y un objeto de destino asignable a ese tipo. Un mtodo de instancia de un tipo, con el parmetro this oculto expuesto en la lista de parmetros formales. Se dice que el delegado es un delegado de instancia abierto. Un mtodo esttico. Un mtodo esttico y un objeto de destino asignable al primer parmetro del mtodo. Se dice que el delegado se cierra en su primer argumento.

Para obtener ms informacin sobre el enlace de delegados, vea Delegados del sistema de tipos comn y CreateDelegate(Type, Object, MethodInfo, Boolean).

Nota: En las versiones 1.0 y 1.1 de .NET Framework, un delegado slo puede representar un mtodo si la firma del mtodo coincide exactamente con la firma que especifica el tipo de delegado. Por tanto, slo se admiten los puntos primero y tercero de la anterior lista y, en el caso del primero, es necesaria una coincidencia exacta de tipos.

Cuando un delegado representa un mtodo de instancia cerrado en su primer argumento (el caso ms habitual), el delegado almacena una referencia al punto de entrada del mtodo y una referencia a un objeto, el objeto de destino, que es de un tipo asignable al tipo que define el mtodo. Cuando un delegado representa un mtodo de instancia abierto, almacena una referencia al punto de entrada del mtodo. La firma del delegado debe incluir el parmetro this oculto en su lista de parmetros formales; en este caso, el delegado no tiene una referencia a un objeto de destino, por lo que se debe proporcionar un objeto de destino al invocar al delegado. Cuando un delegado representa un mtodo esttico, el delegado almacena una referencia al punto de entrada del mtodo. Cuando un delegado representa un mtodo esttico cerrado en su primer argumento, el delegado almacena una referencia al punto de entrada del mtodo y una referencia a un objeto de destino asignable al tipo del primer argumento del mtodo. Al invocar al delegado, el primer argumento del mtodo esttico recibe el objeto de destino. La lista de invocaciones de un delegado es un conjunto ordenado de delegados donde cada elemento de la lista invoca exactamente a uno de los mtodos representados por el delegado. Una lista de invocaciones puede contener mtodos duplicados. Durante una invocacin, los mtodos se llaman en el orden en que aparecen en la lista de invocaciones. Un delegado intenta invocar a todos los mtodos de la lista de invocaciones; se invoca a los duplicados una vez por cada aparicin en la lista de invocaciones. Los delegados son inmutables; una vez creados, la lista de invocaciones de un delegado no cambia. Se dice que los delegados son de multidifusin y combinables, porque un delegado puede invocar uno o varios mtodos y se puede utilizar para combinar operaciones. Las operaciones de combinacin, como Combine y Remove, no modifican los delegados existentes. En lugar de ello, una operacin de este tipo devuelve un nuevo delegado que contiene los resultados de la operacin, un delegado sin modificar o null. Una operacin de combinacin devuelve null cuando el resultado de la operacin es un delegado que no hace referencia a un mtodo como mnimo. Una operacin de combinacin devuelve un delegado sin modificar cuando la operacin solicitada no tiene efecto. Si un mtodo invocado produce una excepcin, el mtodo deja de ejecutarse, la excepcin se pasa de nuevo al llamador del delegado y no se invoca al resto de los mtodos de la lista de invocaciones. Aunque la excepcin se detecte en el llamador, este comportamiento no cambia. Cuando la firma de los mtodos invocados por un delegado incluye un valor devuelto, el delegado devuelve el valor devuelto del ltimo elemento de la lista de invocaciones. Cuando la firma incluye un parmetro que se pasa por referencia, el valor final del parmetro es el resultado que se obtiene despus de que cada mtodo de la lista de invocaciones se ejecute secuencialmente y actualice el valor del parmetro. El equivalente que ms se asemeja a un delegado en C o C++ es un puntero a una funcin. Un delegado puede representar un mtodo esttico o un mtodo de instancia. Cuando representa un mtodo de instancia, el delegado no slo almacena una referencia al punto de entrada del mtodo, sino que tambin almacena una referencia a la instancia de clase. A diferencia de los punteros a funciones, los delegados estn orientados a objetos y presentan seguridad de tipos.

Ejemplos

En el ejemplo siguiente se muestra la forma de definir un delegado denominado myMethodDelegate. Las instancias de este delegado se crean para un mtodo de instancia y un mtodo esttico de la clase mySampleClass anidada. El delegado correspondiente al mtodo de instancia requiere una instancia de mySampleClass. La instancia de mySampleClass se guarda en una variable denominada mySC. using System; public class SamplesDelegate { // Declares a delegate for a method that takes in an int and returns a String. public delegate String myMethodDelegate( int myInt ); // Defines some methods to which the delegate can point. public class mySampleClass { // Defines an instance method. public String myStringMethod ( int myInt ) { if ( myInt > 0 ) return( "positive" ); if ( myInt < 0 ) return( "negative" ); return ( "zero" ); } // Defines a static method. public static String mySignMethod ( int myInt ) { if ( myInt > 0 ) return( "+" ); if ( myInt < 0 ) return( "-" ); return ( "" ); } } public static void Main() { // Creates one delegate for each method. For the instance method, an // instance (mySC) must be supplied. For the static method, use the // class name. mySampleClass mySC = new mySampleClass(); myMethodDelegate myD1 = new myMethodDelegate( mySC.myStringMethod ); myMethodDelegate myD2 = new myMethodDelegate( mySampleClass.mySignMethod ); // Invokes the delegates. Console.WriteLine( "{0} is {1}; use the sign \"{2}\".", 5, myD1( 5 ), myD2( 5 ) ); Console.WriteLine( "{0} is {1}; use the sign \"{2}\".", -3, myD1( -3 ), myD2( -3 ) ); Console.WriteLine( "{0} is {1}; use the sign \"{2}\".", 0, myD1( 0 ), myD2( 0 ) ); } } /* This code produces the following output:

5 is positive; use the sign "+". -3 is negative; use the sign "-". 0 is zero; use the sign "". */ Jerarqua de herencia System.Object System.Delegate System.MulticastDelegate Seguridad para subprocesos Todos los miembros static (Shared en Visual Basic) pblicos de este tipo son seguros para la ejecucin de subprocesos. No se garantiza que los miembros de instancias sean seguros para la ejecucin de subprocesos.

Utilizar delegados (Gua de programacin de C#) Un delegado es un tipo de objeto que encapsula un mtodo de forma segura, similar a un puntero a funcin de C y C++. A diferencia de los punteros a funcin de C, los delegados estn orientados a objetos, proporcionan seguridad de tipos y son seguros. El tipo de un delegado se define por su nombre. El ejemplo siguiente declara un delegado denominado Del que puede encapsular un mtodo que toma un valor string como argumento y devuelve un valor void: public delegate void Del(string message);

Generalmente, un objeto de delegado se crea proporcionando el nombre del mtodo que el delegado contendr o con un mtodo annimo. Una vez que se crean instancias de un delegado, el delegado pasar al mtodo una llamada realizada por ste al delegado. Los parmetros que el llamador pas al delegado se pasan al mtodo y el delegado devuelve al llamador el valor devuelto del mtodo, si hay alguno. Esto se conoce como invocar al delegado. Se puede invocar un delegado con instancias como si fuera el propio mtodo contenido. Por ejemplo: // Create a method for a delegate. public static void DelegateMethod(string message) { System.Console.WriteLine(message); }

// Instantiate the delegate. Del handler = DelegateMethod; // Call the delegate. handler("Hello World");

Los tipos de delegados se derivan de la clase Delegate en .NET Framework. Los tipos de delegados son sealed (no se pueden derivar) y no es posible derivar clases personalizadas de Delegate. Puesto que el

delegado con instancias es un objeto, puede pasarse como parmetro o asignarse a una propiedad. Esto permite que un mtodo acepte un delegado como parmetro y llame al delegado posteriormente. Esto se conoce como devolucin de llamada asincrnica y constituye un mtodo comn de notificacin de un llamador cuando ha finalizado un proceso largo. Cuando se utiliza un delegado de esta forma, no es necesario que el cdigo que utiliza el delegado conozca la implementacin del mtodo que se est utilizando. La funcionalidad es similar a la encapsulacin que proporcionan las interfaces. Para obtener ms informacin, vea Cundo se utilizan delegados en lugar de interfaces (Gua de programacin de C#). Otro uso comn de devoluciones de llamada es definir un mtodo de comparacin personalizado y pasar ese delegado a un mtodo de ordenacin. Esto permite al cdigo del llamador ser parte del algoritmo de ordenacin. El mtodo del ejemplo siguiente utiliza el tipo Del como parmetro: public void MethodWithCallback(int param1, int param2, Del callback) { callback("The number is: " + (param1 + param2).ToString()); } A continuacin, se puede pasar el delegado creado anteriormente a ese mtodo: MethodWithCallback(1, 2, handler);

y recibir el resultado siguiente en la consola: The number is: 3 Al utilizar el delegado como abstraccin, MethodWithCallback no es necesario llamar a la consola directamente; es decir, el delegado no se tiene que disear pensando en una consola. Lo que MethodWithCallback hace es simplemente preparar una cadena y pasarla a otro mtodo. Esto es especialmente eficaz, puesto que un mtodo delegado puede utilizar cualquier nmero de parmetros. Cuando se crea un delegado para contener un mtodo de instancia, el delegado hace referencia tanto a la instancia como al mtodo. Un delegado no conoce el tipo de instancia a parte del mtodo que ste contiene, de modo que un delegado puede hacer referencia a cualquier tipo de objeto siempre que exista un mtodo en dicho objeto que coincida con la firma del delegado. Cuando se crea un delegado para contener un mtodo esttico, ste slo hace referencia al mtodo. Considere las siguientes declaraciones: public class MethodClass { public void Method1(string message) { } public void Method2(string message) { } } Junto con el mtodo esttico DelegateMethod que se mostr previamente, ahora tenemos tres mtodos que la instancia de Del puede contener. Un delegado puede llamar a ms de un mtodo cuando se invoca. Esto se denomina multidifusin. Para agregar un mtodo adicional a la lista de mtodos del delegado (lista de invocacin), simplemente es necesario agregar dos delegados mediante los operadores de suma o de asignacin de suma ('+' o '+='). Por ejemplo: MethodClass obj = new MethodClass(); Del d1 = obj.Method1; Del d2 = obj.Method2; Del d3 = DelegateMethod;

//Both types of assignment are valid. Del allMethodsDelegate = d1 + d2; allMethodsDelegate += d3;

En este momento, allMethodsDelegate contiene tres mtodos en su lista de invocacin: Method1, Method2 y DelegateMethod. Los tres delegados originales, d1, d2 y d3 no cambian. Cuando se invoca a allMethodsDelegate, se llama a los tres mtodos por orden. Si el delegado utiliza parmetros de referencia, sta a su vez se pasa secuencialmente a cada uno de los tres mtodos y todos los cambios efectuados por un mtodo son visibles para el siguiente mtodo. Cuando alguno de los mtodos produce una excepcin que no se detecta dentro del mtodo, esa excepcin se pasa al llamador del delegado y no se llama a ninguno de los mtodos siguientes de la lista de invocacin. Si el delegado tiene un valor devuelto y/o fuera de los parmetros, devuelve el valor devuelto y los parmetros del ltimo mtodo invocado. Para quitar un mtodo de la lista de invocacin, utilice el operador de resta o de asignacin de resta ('-' o '-='). Por ejemplo: //remove Method1 allMethodsDelegate -= d1; // copy AllMethodsDelegate while removing d2 Del oneMethodDelegate = allMethodsDelegate - d2;

Puesto que los tipos de delegados se derivan de System.Delegate, los mtodos y las propiedades definidos por esa clase se pueden llamar en el delegado. Por ejemplo, para buscar el nmero de mtodos en la lista de invocacin de un delegado, puede escribir: int invocationCount = d1.GetInvocationList().GetLength(0);

Los delegados con ms de un mtodo en su lista de invocacin derivan de MulticastDelegate, que es una subclase de System.Delegate. El cdigo anterior funciona en ambos casos porque las dos clases admiten GetInvocationList. Los delegados de multidifusin se utilizan ampliamente en el control de eventos. Los objetos de origen de eventos envan notificaciones de eventos a objetos de destinatario registrados para recibir ese evento. Para registrar un evento, el destinatario crea un mtodo diseado para controlar el evento, a continuacin crea un delegado para dicho mtodo y pasa al delegado al origen de eventos. El origen llama al delegado cuando se produce el evento. Luego el delegado llama al mtodo de control de eventos del destinatario y entrega los datos del evento. El origen de eventos define el tipo de delegado para un evento dado. Para obtener ms informacin, vea Eventos (Gua de programacin de C#). La comparacin de delegados de dos tipos distintos asignados en tiempo de compilacin producir un error de compilacin. Si las instancias de delegado son estticamente del tipo System.Delegate, se permite la comparacin, pero se devolver false en tiempo de ejecucin. Por ejemplo: delegate void Delegate1(); delegate void Delegate2(); static void method(Delegate1 d, Delegate2 e, System.Delegate f) { // Compile-time error. //Console.WriteLine(d == e); // OK at compile-time. False if the run-time type of f // is not the same as that of d.

System.Console.WriteLine(d == f); }

Cundo se utilizan delegados en lugar de interfaces (Gua de programacin de C#) Tanto los delegados como las interfaces permiten a un diseador de clases separar las declaraciones y las implementaciones de los tipos. Una interfaz determinada puede ser heredada e implementada por cualquier clase o estructura. Se puede crear un delegado para un mtodo en cualquier clase, con tal de que el mtodo se ajuste a la firma de mtodo para el delegado. Cualquier objeto puede utilizar una referencia de interfaz o un delegado sin tener conocimiento alguno sobre la clase que implementa el mtodo de interfaz o delegado. Segn estas similitudes, cundo debera un diseador de clases utilizar un delegado y cundo una interfaz? Utilice un delegado cuando: Se utilice un modelo de diseo de eventos. Se prefiere a la hora de encapsular un mtodo esttico. El autor de las llamadas no tiene ninguna necesidad de obtener acceso a otras propiedades, mtodos o interfaces en el objeto que implementa el mtodo. Se desea conseguir una composicin sencilla. Una clase puede necesitar ms de una implementacin del mtodo.

Utilice una interfaz cuando: Haya un grupo de mtodos relacionados a los que se pueda llamar. Una clase slo necesita una implementacin del mtodo. La clase que utiliza la interfaz desear convertir esa interfaz en otra interfaz o tipos de clase. El mtodo que se va a implementar est vinculado al tipo o identidad de la clase; por ejemplo, mtodos de comparacin.

Un buen ejemplo del uso de una interfaz de mtodo nico en lugar de un delegado es IComparable o la versin genrica, IComparable<T>. IComparable declara el mtodo CompareTo, que devuelve un entero que especifica una relacin menor que, igual que o mayor que entre dos objetos del mismo tipo. IComparable se puede utilizar como base de un algoritmo de ordenacin. Aunque utilizar un mtodo de comparacin delegado como la base de un algoritmo de ordenacin sera vlido, no es lo ideal. Lo ideal es una interfaz de mtodo nico, ya que la capacidad de establecer comparaciones pertenece a la clase, y el algoritmo de comparacin no cambia en tiempo de ejecucin.

Delegados con mtodos con nombre y delegados con mtodos annimos (Gua de programacin de C#) Se puede asociar un delegado a un mtodo con nombre. Cuando se crean instancias de un delegado mediante un mtodo con nombre, el mtodo se pasa como parmetro; por ejemplo: // Declare a delegate: delegate void Del(int x); // Define a named method: void DoWork(int k) { /* ... */ }

// Instantiate the delegate using the method as a parameter: Del d = obj.DoWork; Esto se denomina utilizar un mtodo con nombre. Los delegados creados con un mtodo con nombre pueden encapsular un mtodo esttico o un mtodo de instancia. Los mtodos con nombre son la nica forma de crear instancias de un delegado en versiones anteriores de C#. Sin embargo, en una situacin en la que crear un mtodo nuevo constituye una sobrecarga no deseada, C# permite crear instancias de un delegado y especificar inmediatamente un bloque de cdigo que el delegado procesar cuando se le llame. El bloque puede contener una expresin lambda o un mtodo annimo. Para obtener ms informacin, vea Funciones annimas (Gua de programacin de C#). Comentarios

El mtodo, que se pasa como parmetro de delegado, debe tener la misma firma que la declaracin de delegado. La instancia de un delegado puede encapsular un mtodo esttico o de instancia. Aunque el delegado puede utilizar un parmetro out, no se recomienda su uso con delegados de eventos de multidifusin porque no se puede saber a qu delegado se va a llamar. Ejemplo 1

El siguiente es un ejemplo sencillo de declaracin y uso de un delegado. Observe que tanto el delegado, Del, como el mtodo asociado, MultiplyNumbers, tienen la misma firma // Declare a delegate delegate void Del(int i, double j); class MathClass { static void Main() { MathClass m = new MathClass(); // Delegate instantiation using "MultiplyNumbers" Del d = m.MultiplyNumbers; // Invoke the delegate object. System.Console.WriteLine("Invoking the delegate using 'MultiplyNumbers':"); for (int i = 1; i <= 5; i++) { d(i, 2); } // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } // Declare the associated method. void MultiplyNumbers(int m, double n) { System.Console.Write(m * n + " ");

} } /* Output: Invoking the delegate using 'MultiplyNumbers': 2 4 6 8 10 */

Ejemplo 2

En el siguiente ejemplo, un delegado se asigna a mtodos estticos y de instancia y devuelve informacin especfica de cada uno de ellos. // Declare a delegate delegate void Del(); class SampleClass { public void InstanceMethod() { System.Console.WriteLine("A message from the instance method."); } static public void StaticMethod() { System.Console.WriteLine("A message from the static method."); } } class TestSampleClass { static void Main() { SampleClass sc = new SampleClass(); // Map the delegate to the instance method: Del d = sc.InstanceMethod; d(); // Map to the static method: d = SampleClass.StaticMethod; d(); } } /* Output: A message from the instance method. A message from the static method. */

Mtodos annimos (Gua de programacin de C#) En versiones de C# anteriores a la versin 2.0, la nica manera de declarar un delegado era utilizar mtodos con nombre. C# 2.0 introdujo los mtodos annimos, mientras que, en C# 3.0 y versiones

posteriores, las expresiones lambda reemplazan a los mtodos annimos como la manera preferente de escribir cdigo insertado. No obstante, la informacin sobre los mtodos annimos de este tema tambin se aplica a las expresiones lambda. Hay un caso en el que un mtodo annimo proporciona una funcionalidad que no se encuentra en las expresiones lambda. Los mtodos annimos permiten omitir la lista de parmetros, y esto significa que un mtodo annimo se puede convertir en delegados con diversas firmas. Esto no es posible con expresiones lambda. Para obtener ms informacin sobre las expresiones lambda, vea Expresiones lambda (Gua de programacin de C#). La creacin de mtodos annimos es bsicamente una forma de pasar un bloque de cdigo como parmetro de delegado. A continuacin se describen dos ejemplos de esto: // Create a handler for a click event button1.Click += delegate(System.Object o, System.EventArgs e) { System.Windows.Forms.MessageBox.Show("Click!"); }; // Create a delegate instance delegate void Del(int x); // Instantiate the delegate using an anonymous method Del d = delegate(int k) { /* ... */ };

Mediante los mtodos annimos, se reduce la sobrecarga de codificacin a la hora de crear instancias de delegados, ya que no es necesario crear un mtodo independiente. Por ejemplo, especificar un bloque de cdigo en vez de un delegado puede ser til en el caso de que la creacin de un mtodo pueda suponer una sobrecarga innecesaria. Un buen ejemplo es cuando se inicia un nuevo subproceso. Esta clase crea un subproceso y tambin contiene el cdigo que el subproceso ejecuta sin crear un mtodo adicional para el delegado. void StartThread() { System.Threading.Thread t1 = new System.Threading.Thread (delegate() { System.Console.Write("Hello, "); System.Console.WriteLine("World!"); }); t1.Start(); } Comentarios

El mbito de los parmetros de un mtodo annimo es el bloque del mtodo annimo. Es un error utilizar una instruccin de salto, como goto, break o continue, en un bloque de mtodo annimo si el destino est fuera del bloque. Tambin es un error utilizar una instruccin de salto, como goto, break o continue, fuera de un bloque de mtodo annimo si el destino est dentro del bloque. Las variables locales y los parmetros cuyo mbito contiene una declaracin de mtodo annimo se denominan variables externas del mtodo annimo. Por ejemplo, en el segmento de cdigo siguiente, n es una variable externa: int n = 0; Del d = delegate() { System.Console.WriteLine("Copy #:{0}", ++n); };

A diferencia de las variables locales, el perodo de duracin de la variable capturada se extiende hasta que los delegados que hacen referencia a los mtodos annimos cumplan con los requisitos para la recoleccin de elementos no utilizados. En el momento en que se crea el delegado, se captura el valor de n. Un mtodo annimo no puede tener acceso a los parmetros ref u out de un mbito externo. No se puede obtener acceso a cdigo no seguro dentro del bloque de mtodo annimo. No se permite el uso de mtodos annimos en el lado izquierdo del operador is. Ejemplo

El ejemplo siguiente muestra las dos maneras de crear instancias de un delegado: Asociar el delegado a un mtodo annimo. Asociar el delegado a un mtodo con nombre (DoWork).

En cada uno de los casos, se muestra un mensaje cuando se invoca al delegado. // Declare a delegate delegate void Printer(string s); class TestClass { static void Main() { // Instatiate the delegate type using an anonymous method: Printer p = delegate(string j) { System.Console.WriteLine(j); }; // Results from the anonymous delegate call: p("The delegate using the anonymous method is called."); // The delegate instantiation using a named method "DoWork": p = new Printer(TestClass.DoWork); // Results from the old style delegate call: p("The delegate using the named method is called."); } // The method associated with the named delegate: static void DoWork(string k) { System.Console.WriteLine(k); } } /* Output: The delegate using the anonymous method is called. The delegate using the named method is called. */

Covarianza y contravarianza en los delegados (Gua de programacin de C#) La covarianza y la contravarianza proporcionan cierta flexibilidad al confrontar las firmas de mtodos con tipos de delegado. La covarianza permite que un mtodo tenga un tipo de valor devuelto ms derivado que

lo que se define en el delegado. La contravarianza permite un mtodo con tipos de parmetro que se deriven menos que en el tipo de delegado. Ejemplo 1 (covarianza)

Descripcin
Este ejemplo muestra cmo se pueden utilizar los delegados con mtodos que tienen tipos de valor devueltos que se derivan del tipo de valor devuelto en la firma de delegado. El tipo de datos devuelto por SecondHandler es de tipo Dogs, que se deriva del tipo Mammals definido en el delegado. class Mammals { } class Dogs : Mammals { } class Program { // Define the delegate. public delegate Mammals HandlerMethod(); public static Mammals FirstHandler() { return null; } public static Dogs SecondHandler() { return null; } static void Main() { HandlerMethod handler1 = FirstHandler; // Covariance allows this delegate. HandlerMethod handler2 = SecondHandler; } }

Ejemplo 2 (contravarianza)

Descripcin

Este ejemplo muestra cmo se pueden utilizar los delegados con mtodos que tienen parmetros de un tipo que son tipos base del tipo de parmetro de la firma de delegado. Con la contravarianza, puede utilizar ahora un controlador de eventos en lugares donde anteriormente tena que utilizar controladores distintos. Por ejemplo, ahora puede crear un controlador de eventos que acepte un parmetro de entrada EventArgs y utilizarlo con el evento Button.MouseClick que enva un tipo MouseEventArgs como parmetro, y tambin con el evento TextBox.KeyDown que enva un parmetro KeyEventArgs. System.DateTime lastActivity; public Form1() { InitializeComponent() lastActivity = new System.DateTime(); this.textBox1.KeyDown += this.MultiHandler; //works with KeyEventArgs this.button1.MouseClick += this.MultiHandler; //works with MouseEventArgs }

// Event hander for any event with an EventArgs or // derived class in the second parameter private void MultiHandler(object sender, System.EventArgs e) { lastActivity = System.DateTime.Now; }

Cmo: Combinar delegados (delegados de multidifusin) (Gua de programacin de C#) En este ejemplo se muestra cmo componer delegados de multidifusin. Una propiedad til de los objetos delegate es que se pueden asignar a una instancia del delegado la multidifusin con el operador +. Un delegado compuesto llama a los dos delegados de los que se compone. Solo pueden ser compuestos los delegados del mismo tipo. El operador - se puede utilizar para quitar un delegado componente de un delegado compuesto. Ejemplo delegate void Del(string s); class TestClass { static void Hello(string s) {

System.Console.WriteLine(" Hello, {0}!", s); } static void Goodbye(string s) { System.Console.WriteLine(" Goodbye, {0}!", s); } static void Main() { Del a, b, c, d; // Create the delegate object a that references // the method Hello: a = Hello; // Create the delegate object b that references // the method Goodbye: b = Goodbye; // The two delegates, a and b, are composed to form c: c = a + b; // Remove a from the composed delegate, leaving d, // which calls only the method Goodbye: d = c - a; System.Console.WriteLine("Invoking a("A"); System.Console.WriteLine("Invoking b("B"); System.Console.WriteLine("Invoking c("C"); System.Console.WriteLine("Invoking d("D"); } } /* Output: Invoking delegate Hello, A! Invoking delegate Goodbye, B! Invoking delegate Hello, C! Goodbye, C! Invoking delegate Goodbye, D! */ delegate a:"); delegate b:"); delegate c:"); delegate d:");

a: b: c: d:

Cmo: Declarar un delegado, crear instancias del mismo y utilizarlo (Gua de programacin de C#) En C# 1.0 y versiones posteriores, los delegados se pueden declarar como se muestra aqu: public delegate void Del<T>(T item);

public void Notify(int i) { } Del<int> d1 = new Del<int>(Notify); En C# 2.0 y versiones posteriores, tambin es posible utilizar un mtodo annimo para declarar e inicializar un delegado mediante esta sintaxis simplificada: Del<int> d2 = Notify; En C# 3.0 y versiones posteriores, tambin es posible declarar delegados y crear instancias de ellos mediante una expresin lambda. Para obtener ms informacin, vea Expresiones lambda (Gua de programacin de C#). El siguiente ejemplo ilustra la declaracin, creacin de instancias y uso de un delegado. La clase BookDB encapsula una base de datos de los libros de una librera. Expone un mtodo ProcessPaperbackBooks, el cual busca todos los libros en edicin rstica de la base de datos y llama a un delegado para cada uno. El tipo delegate que se utiliza se denomina ProcessBookDelegate. La clase Test utiliza esta clase para imprimir los ttulos y el precio medio de los libros en edicin rstica. El uso de delegados promueve una buena separacin de la funcionalidad entre la base de datos de la librera y el cdigo del programa cliente. El cdigo del cliente no tiene conocimiento de cmo estn almacenados los libros ni de cmo busca el cdigo de la librera de los libros en rstica. El cdigo de la librera no conoce qu procesamiento se realiza sobre los libros en rstica despus de encontrarlos. Ejemplo // A set of classes for handling a bookstore: namespace Bookstore { using System.Collections; // Describes a book in the book list: public struct Book { public string Title; // Title of the book. public string Author; // Author of the book. public decimal Price; // Price of the book. public bool Paperback; // Is it paperback? public Book(string title, string author, decimal price, bool paperBack) { Title = title; Author = author; Price = price; Paperback = paperBack; } } // Declare a delegate type for processing a book: public delegate void ProcessBookDelegate(Book book); // Maintains a book database. public class BookDB { // List of all books in the database: ArrayList list = new ArrayList();

// Add a book to the database: public void AddBook(string title, string author, decimal price, bool paperBack) { list.Add(new Book(title, author, price, paperBack)); } // Call a passed-in delegate on each paperback book to process it: public void ProcessPaperbackBooks(ProcessBookDelegate processBook) { foreach (Book b in list) { if (b.Paperback) // Calling the delegate: processBook(b); } } } } // Using the Bookstore classes: namespace BookTestClient { using Bookstore; // Class to total and average prices of books: class PriceTotaller { int countBooks = 0; decimal priceBooks = 0.0m; internal void AddBookToTotal(Book book) { countBooks += 1; priceBooks += book.Price; } internal decimal AveragePrice() { return priceBooks / countBooks; } } // Class to test the book database: class TestBookDB { // Print the title of the book. static void PrintTitle(Book b) { System.Console.WriteLine(" {0}", b.Title); } // Execution starts here. static void Main() { BookDB bookDB = new BookDB();

// Initialize the database with some books: AddBooks(bookDB); // Print all the titles of paperbacks: System.Console.WriteLine("Paperback Book Titles:"); // Create a new delegate object associated with the static // method Test.PrintTitle: bookDB.ProcessPaperbackBooks(PrintTitle); // Get the average price of a paperback by using // a PriceTotaller object: PriceTotaller totaller = new PriceTotaller(); // Create a new delegate object associated with the nonstatic // method AddBookToTotal on the object totaller: bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal); System.Console.WriteLine("Average Paperback Book Price: ${0:#.##}", totaller.AveragePrice()); } // Initialize the book database with some test books: static void AddBooks(BookDB bookDB) { bookDB.AddBook("The C Programming Language", "Brian W. Kernighan and Dennis M. Ritchie", 19. 95m, true); bookDB.AddBook("The Unicode Standard 2.0", "The Unicode Consortium", 39.95m, true); bookDB.AddBook("The MS-DOS Encyclopedia", "Ray Duncan", 129.95m, false); bookDB.AddBook("Dogbert's Clues for the Clueless", "Scott Adams", 12.00m, true); } } } /* Output: Paperback Book Titles: The C Programming Language The Unicode Standard 2.0 Dogbert's Clues for the Clueless Average Paperback Book Price: $23.97 */

Programacin eficaz

Declarar un delegado. La instruccin siguiente declara un nuevo tipo delegado. public delegate void ProcessBookDelegate(Book book);

Cada tipo delegado describe el nmero y tipo de los argumentos, as como el tipo del valor devuelto de los mtodos que puede encapsular. Cuando se necesita un nuevo conjunto de tipos de argumentos o de valor devuelto, se debe declarar un nuevo tipo delegado. Crear instancias de un delegado.

Una vez declarado un tipo delegado, debe crearse un objeto delegado y asociarlo con un determinado mtodo. En el ejemplo anterior, esto se hace pasando el mtodo PrintTitle al mtodo ProcessPaperbackBooks como en el ejemplo siguiente: bookDB.ProcessPaperbackBooks(PrintTitle);

Esto crea un nuevo objeto delegado asociado con el mtodo esttico Test.PrintTitle. De igual forma, el mtodo no esttico AddBookToTotal del objeto totaller se pasa como en el ejemplo siguiente: bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);

En ambos casos, este nuevo objeto delegado se pasa al mtodo ProcessPaperbackBooks. Una vez que se crea el delegado, el mtodo con el que est asociado no cambia nunca; los objetos delegados son inmutables. Llamar a un delegado. Una vez creado un objeto delegado, ste se pasa normalmente a otro cdigo que llamar al delegado. La llamada a un objeto delegado se realiza mediante el nombre del objeto delegado, seguido por los argumentos entre parntesis que se pasarn al delegado. A continuacin, se muestra un ejemplo de una llamada a delegado: processBook(b);

Se puede llamar a un delegado de forma sincrnica, como en este ejemplo, o de forma asincrnica, utilizando los mtodos BeginInvoke y EndInvoke.

EventHandler (Delegado)
Representa el mtodo que controlar eventos que no tienen datos de evento. Espacio de nombres: System Ensamblado: mscorlib (en mscorlib.dll)

Sintaxis

[SerializableAttribute] [ComVisibleAttribute(true)] public delegate void EventHandler( Object sender, EventArgs e ) Parmetros sender

Tipo: System.Object Origen del evento. e Tipo: System.EventArgs Clase EventArgs que no contiene datos de evento.

Comentarios

El modelo de evento de .NET Framework se basa en la existencia de un delegado de eventos que conecte un evento a su controlador. Para provocar un evento, se requieren dos elementos:

Delegado que identifica el mtodo que proporciona la respuesta al evento. Clase que contiene los datos de eventos.

El delegado es un tipo que define una firma, es decir, el tipo del valor devuelto y los tipos de lista de parmetros de un mtodo. Se puede utilizar el tipo de delegado para declarar una variable que puede hacer referencia a cualquier mtodo con la misma firma que el delegado. La firma estndar de un delegado del controlador de eventos define un mtodo que no devuelve ningn valor, cuyo primer parmetro es del tipo Object y hace referencia a la instancia que provoca el evento y cuyo segundo parmetro se deriva del tipo EventArgs y contiene los datos de evento. Si el evento no genera los datos de evento, el segundo parmetro simplemente es una instancia de EventArgs. De lo contrario, el segundo parmetro es un tipo personalizado derivado de EventArgs y proporciona los campos o las propiedades necesarias para contener los datos de evento. EventHandler es un delegado predefinido que representa especficamente un mtodo controlador para un evento que no genera datos. Si su evento genera datos, debe suministrar su propio tipo de datos de evento personalizado y crear un delegado donde el tipo del segundo parmetro sea el tipo personalizado o utilizar la clase de delegado genrico EventHandler<TEventArgs> y sustituir el tipo personalizado por el parmetro de tipo genrico. Para asociar el evento al mtodo que lo controlar, hay que agregar una instancia del delegado al evento. Siempre que se produce el evento, se llama al controlador de eventos, a menos que se quite el delegado. Para obtener ms informacin sobre los delegados del controlador de eventos, vea Eventos y delegados.

Topic
Cmo: Enlazar controladores de eventos dinmicamente en tiempo de ejecucin en las pginas Web ASP.NET Cmo: Enlazar controladores de eventos dinmicamente en tiempo de ejecucin en las pginas Web ASP.NET Cmo: Enlazar controladores de eventos dinmicamente en tiempo de ejecucin en las pginas web ASP.NET

Location
Generar aplicaciones Web ASP .NET

Generar aplicaciones Web ASP .NET en Visual Studio dv_vwdcon

Ejemplos
En el ejemplo de cdigo siguiente se muestra la declaracin de un delegado del controlador de eventos que no utiliza datos de evento. La clase EventHandler es el tipo del delegado del evento, sender es el objeto que provoca el evento y e es un objeto de

datos de evento que no contiene datos. La segunda lnea de cdigo del ejemplo define el miembro del evento de la clase para un evento que no tiene datos.

public delegate void EventHandler(Object sender, EventArgs e); public event EventHandler NoDataEventHandler; EventArgs (Clase)
EventArgs es la clase base para las clases que contienen datos de eventos. Espacio de nombres: System Ensamblado: mscorlib (en mscorlib.dll)

Sintaxis
[SerializableAttribute] [ComVisibleAttribute(true)] public class EventArgs

Comentarios
Esta clase no contiene datos de eventos; utilizan esta clase los eventos que no pasan informacin de estado a un controlador de eventos cuando se produce un evento. Si el controlador de eventos requiere informacin de estado, la aplicacin debe derivar una clase a partir de sta para incluir los datos. Por ejemplo, la clase System.AssemblyLoadEventArgs se utiliza para incluir los datos de los eventos de carga de ensamblados y contiene un System.Reflection.Assembly que describe el ensamblado cargado. Para obtener ms informacin sobre eventos, vea el tema EventHandler.

Ejemplos
En el siguiente ejemplo de cdigo se muestra el uso de EventArgs. En este ejemplo, FireEventArgs es un conjunto de argumentos de evento derivado de EventArgs que se ha pasado a FireEventHandler cuando se ha provocado un evento al llamar a ActivateFireAlarm.

using System; // FireEventArgs: a custom event inherited from EventArgs. public class FireEventArgs: EventArgs { public FireEventArgs(string room, int ferocity) { this.room = room; this.ferocity = ferocity; } // The fire event will have two pieces of information-// 1) Where the fire is, and 2) how "ferocious" it is. public string room;

public int ferocity; } //end of class FireEventArgs

// Class with a function that creates the eventargs and initiates the event public class FireAlarm { // Events are handled with delegates, so we must establish a FireEventHandler // as a delegate: public delegate void FireEventHandler(object sender, FireEventArgs fe); // Now, create a public event "FireEvent" whose type is our FireEventHandler delegate. public event FireEventHandler FireEvent; // This will be the starting point of our event-- it will create FireEventArgs, // and then raise the event, passing FireEventArgs. public void ActivateFireAlarm(string room, int ferocity) { FireEventArgs fireArgs = new FireEventArgs(room, ferocity); // Now, raise the event by invoking the delegate. Pass in // the object that initated the event (this) as well as FireEventArgs. // The call must match the signature of FireEventHandler. FireEvent(this, fireArgs); } // end of class FireAlarm

// Class which handles the event class FireHandlerClass { // Create a FireAlarm to handle and raise the fire events. public FireHandlerClass(FireAlarm fireAlarm) {

// Add a delegate containing the ExtinguishFire function to the class' // event so that when FireAlarm is raised, it will subsequently execute // ExtinguishFire. fireAlarm.FireEvent += new FireAlarm.FireEventHandler(ExtinguishFire); } // This is the function to be executed when a fire event is raised. void ExtinguishFire(object sender, FireEventArgs fe) { Console.WriteLine("\nThe ExtinguishFire function was called by {0}.", sender.ToStr ing()); // Now, act in response to the event. if (fe.ferocity < 2) Console.WriteLine("This fire in the {0} is no problem. some water on it.", fe.room);

I'm going to pour

else if (fe.ferocity < 5) Console.WriteLine("I'm using FireExtinguisher to put out the fire in the { 0}.", fe.room); else Console.WriteLine("The fire in the {0} is out of control. fire department!", fe.room); } } //end of class FireHandlerClass public class FireEventTest { public static void Main () I'm calling the

// Create an instance of the class that will be firing an event. FireAlarm myFireAlarm = new FireAlarm(); // Create an instance of the class that will be handling the event. Note that // it receives the class that will fire the event as a parameter. FireHandlerClass myFireHandler = new FireHandlerClass(myFireAlarm); //use our class to raise a few events and watch them get handled myFireAlarm.ActivateFireAlarm("Kitchen", 3); myFireAlarm.ActivateFireAlarm("Study", 1); myFireAlarm.ActivateFireAlarm("Porch", 5); return; } } //end of main

// end of FireEventTest

Eventos (Gua de programacin de C#) Cuando ocurre algo interesante, los eventos habilitan una clase u objeto para notificarlo a otras clases u objetos. La clase que enva (o produce) el evento recibe el nombre de editor y las clases que reciben (o controlan) el evento se denominan suscriptores. En una aplicacin de formularios Windows Forms o Web en C# tpica, se suscribe a eventos generados por controles como botones y cuadros de lista. Puede utilizar el entorno de desarrollo integrado (IDE) Visual C# para examinar los eventos que publica un control y seleccionar los que desea controlar. El IDE agrega automticamente un mtodo de controlador de eventos vaco y el cdigo para suscribirse al evento. Para obtener ms informacin, vea Cmo: Suscribir y cancelar la suscripcin a eventos (Gua de programacin de C#). Informacin general de eventos

Los eventos tienen las propiedades siguientes:

El editor determina cundo se produce un evento; los suscriptores determinan qu operacin se realiza en respuesta al evento. Un evento puede tener varios suscriptores. Un suscriptor puede controlar varios eventos de varios editores. Nunca se provocan eventos que no tienen suscriptores. Los eventos se suelen usar para sealar acciones del usuario, como hacer clic en un botn o seleccionar un men en interfaces grficas de usuario. Si un evento tiene varios suscriptores, se invocan los controladores de eventos sincrnicamente cuando se produce el evento. Para invocar de forma asincrnica los eventos, vea Llamar a mtodos sincrnicos de forma asincrnica. Los eventos se pueden utilizar para sincronizar subprocesos. En la biblioteca de clases .NET Framework, los eventos se basan en el delegado EventHandler y en la clase base EventArgs.

Secciones relacionadas

Para obtener ms informacin, vea: Cmo: Suscribir y cancelar la suscripcin a eventos (Gua de programacin de C#) Cmo: Publicar eventos que cumplan las directrices de .NET Framework (Gua de programacin de C#) Cmo: Producir eventos de una clase base en clases derivadas (Gua de programacin de C#) Cmo: Implementar eventos de interfaz (Gua de programacin de C#) Sincronizacin de subprocesos (Gua de programacin de C#) Cmo: Utilizar un diccionario para almacenar instancias de eventos (Gua de programacin de C#) Cmo: Implementar descriptores de acceso de eventos personalizados (Gua de programacin de C#) Ejemplo de eventos Diseo de eventos

Especificacin del lenguaje C#

Para obtener ms informacin, vea las secciones siguientes de Especificacin del lenguaje C#: 1.6.7.4 Eventos 10.2.9.2 Nombres de miembros reservados para eventos 10.8 Eventos 13.2.3 Eventos de interfaz

Cmo: Suscribir y cancelar la suscripcin a eventos (Gua de programacin de C#) La suscripcin a un evento publicado por otra clase se realiza cuando se desea escribir cdigo personalizado al que se llama cuando se produce ese evento. Por ejemplo, puede suscribirse al evento click de un botn para que la aplicacin realice alguna operacin cuando el usuario haga clic en el botn.

Para suscribirse a eventos mediante el IDE de Visual Studio


1. Si no puede ver la ventana Propiedades, en la vista Diseo haga clic con el botn secundario del mouse en el formulario o control para el que desea crear un controlador de eventos y seleccione Propiedades. 2. En la parte superior de la ventana Propiedades, haga clic en el icono Eventos. 3. Haga doble clic en el evento que desea crear (por ejemplo, el evento Load).

Visual C# crea un mtodo controlador de eventos vaco y lo agrega al cdigo. Tambin puede agregar manualmente el cdigo en la vista Cdigo. Por ejemplo, las lneas siguientes de cdigo declaran un mtodo controlador de eventos al que se llamar cuando la clase Form genere el evento Load. private void Form1_Load(object sender, System.EventArgs e) { // Add your form load event handling code here. }

La lnea de cdigo que es necesaria para suscribirse al evento tambin se genera automticamente con el mtodo InitializeComponent en el archivo Form1.Designer.cs del proyecto. Se asemeja a lo siguiente: Copiar this.Load += new System.EventHandler(this.Form1_Load);

Para suscribirse a eventos mediante programacin


1. Defina un mtodo controlador de eventos cuya firma coincida con la firma de delegado del evento. Por ejemplo, si el evento se basa en el tipo de delegado EventHandler, el siguiente cdigo representa el cdigo auxiliar del mtodo: Copiar void HandleCustomEvent(object sender, CustomEventArgs a) { // Do something useful here. } 2. Utilice el operador de suma y asignacin (+=) para asociar el controlador de eventos al evento. En el ejemplo siguiente, se asume que un objeto denominado publisher tiene un evento denominado RaiseCustomEvent. Observe que la clase de suscriptor necesita una referencia a la clase de editor para suscribirse a sus eventos. Copiar publisher.RaiseCustomEvent += HandleCustomEvent; Observe que la sintaxis anterior es nueva en C# 2.0. Al igual que en la sintaxis de C# 1.0, el delegado encapsulador debe crearse explcitamente mediante la palabra clave new: Copiar

publisher.RaiseCustomEvent += new CustomEventHandler(HandleCustomEvent); Tambin puede agregarse un controlador de eventos utilizando una expresin lambda: Copiar public Form1() { InitializeComponent(); // Use a lambda expression to define an event handler. this.Click += (s,e) => { MessageBox.Show( ((MouseEventArgs)e).Location.ToString());}; } Para obtener ms informacin, vea Cmo: Usar expresiones lambda fuera de LINQ (Gua de programacin de C#).

Para suscribirse a eventos mediante un mtodo annimo


Si no tiene que cancelar la suscripcin a un evento ms adelante, puede utilizar el operador de suma y asignacin (+=) para asociar un mtodo annimo al evento. En el ejemplo siguiente, se asume que un objeto denominado publisher tiene un evento denominado RaiseCustomEvent y que se ha definido una clase CustomEventArgs para proporcionar algn tipo de informacin especfica del evento. Observe que la clase de suscriptor necesita una referencia a publisher para suscribirse a sus eventos. Copiar publisher.RaiseCustomEvent += delegate(object o, CustomEventArgs e) { string s = o.ToString() + " " + e.ToString(); Console.WriteLine(s); }; Es importante tener en cuenta que puede no resultar fcil cancelar la suscripcin a un evento si se ha utilizado una funcin annima para suscribirse a l. Para cancelar la suscripcin en esta situacin, es necesario regresar al cdigo donde se ha suscrito al evento, almacenar el mtodo annimo en una variable de delegado y, a continuacin, agregar el delegado al evento. En general, se recomienda que no utilice funciones annimas para suscribirse a eventos si va a tener que cancelar la suscripcin al evento en el cdigo ms adelante. Para obtener ms informacin sobre las funciones annimas, vea Funciones annimas (Gua de programacin de C#). Cancelar una suscripcin

Para impedir que se invoque el controlador de eventos cuando se produce el evento, basta con cancelar la suscripcin al evento. Para evitar que se pierdan recursos, debe cancelar la suscripcin a los eventos antes de eliminar un objeto suscriptor. Hasta que se cancela la suscripcin a un evento, el delegado multidifusin subyacente al evento en el objeto de publicacin tiene una referencia al delegado que encapsula el controlador de eventos del suscriptor. Mientras el objeto de publicacin mantenga esa referencia, la recoleccin de elementos no utilizados no eliminar el objeto suscriptor.

Para cancelar la suscripcin a un evento


Utilice el operador de resta y asignacin (-=) para cancelar la suscripcin a un evento: Copiar publisher.RaiseCustomEvent -= HandleCustomEvent; Cuando se haya cancelado la suscripcin a un evento de todos los suscriptores, la instancia del evento en la clase de editor se establecer en null.

Cmo: Publicar eventos que cumplan las directrices de .NET Framework (Gua de programacin de C#) El procedimiento siguiente muestra cmo agregar eventos que cumplan el modelo estndar .NET Framework a sus propias clases y estructuras. Todos los eventos de la biblioteca de clases .NET Framework se basan en el delegado EventHandler, que se define del modo siguiente: Copiar public delegate void EventHandler(object sender, EventArgs e); Nota: .NET Framework 2.0 incluye una versin genrica de este delegado, EventHandler<TEventArgs>. Los ejemplos siguientes muestran cmo utilizar ambas versiones.

Aunque los eventos de las clases que defina se pueden basar en cualquier tipo de delegado vlido, incluidos los delegados que devuelven un valor, normalmente es aconsejable que los eventos se basen en el modelo de .NET Framework a travs de EventHandler, como se muestra en el ejemplo siguiente.

Para publicar eventos basados en el modelo EventHandler


1. (Omita este paso y vaya directamente al paso 3a si no tiene que enviar datos personalizados con el evento). Declare la clase en un mbito que puedan ver las clases de editor y suscriptor y agregue los miembros necesarios para almacenar los datos de eventos personalizados. En este ejemplo, se devuelve una cadena simple. public class CustomEventArgs : EventArgs { public CustomEventArgs(string s)

{ msg = s; } private string msg; public string Message { get { return msg; } } } 2. (Omita este paso si utiliza la versin genrica de EventHandler<TEventArgs>). Declare un delegado en la clase de publicacin. Asgnele un nombre que acabe en EventHandler. El segundo parmetro especifica el tipo EventArgs personalizado. public delegate void CustomEventHandler(object sender, CustomEventArgs a); 3. Declare el evento en la clase de publicacin mediante alguno de los procedimientos siguientes. a. Si no tiene ninguna clase EventArgs personalizada, el tipo Event ser el delegado EventHandler no genrico. No necesita declararlo porque ya est declarado en el espacio de nombres System, que se incluye de forma predeterminada al crear el proyecto de C#: public event EventHandler RaiseCustomEvent; b. Si utiliza la versin no genrica de EventHandler y tiene una clase personalizada derivada de EventArgs, declare el evento dentro de la clase de publicacin y use el delegado como tipo: class Publisher { public event CustomEventHandler RaiseCustomEvent; } c. Si utiliza la versin genrica, no necesita un delegado personalizado. En lugar de ello, especifique el tipo de evento como EventHandler<CustomEventArgs>, como sustituto del nombre de su propia clase incluido entre corchetes angulares. public event EventHandler<CustomEventArgs> RaiseCustomEvent; Ejemplo

En el ejemplo siguiente se muestran los pasos indicados anteriormente con una clase EventArgs personalizada y EventHandler<TEventArgs> como tipo de evento. namespace DotNetEvents { using System; using System.Collections.Generic; // Define a class to hold custom event info public class CustomEventArgs : EventArgs {

public CustomEventArgs(string s) { message = s; } private string message; public string Message { get { return message; } set { message = value; } } } // Class that publishes an event class Publisher { // Declare the event using EventHandler<T> public event EventHandler<CustomEventArgs> RaiseCustomEvent; public void DoSomething() { // Write some code that does something useful here // then raise the event. You can also raise an event // before you execute a block of code. OnRaiseCustomEvent(new CustomEventArgs("Did something")); } // Wrap event invocations inside a protected virtual method // to allow derived classes to override the event invocation behavior protected virtual void OnRaiseCustomEvent(CustomEventArgs e) { // Make a temporary copy of the event to avoid possibility of // a race condition if the last subscriber unsubscribes // immediately after the null check and before the event is raised. EventHandler<CustomEventArgs> handler = RaiseCustomEvent; // Event will be null if there are no subscribers if (handler != null) { // Format the string to send inside the CustomEventArgs parameter e.Message += String.Format(" at {0}", DateTime.Now.ToString()); // Use the () operator to raise the event. handler(this, e); } } } //Class that subscribes to an event class Subscriber { private string id; public Subscriber(string ID, Publisher pub) { id = ID; // Subscribe to the event using C# 2.0 syntax

pub.RaiseCustomEvent += HandleCustomEvent; } // Define what actions to take when the event is raised. void HandleCustomEvent(object sender, CustomEventArgs e) { Console.WriteLine(id + " received this message: {0}", e.Message); } } class Program { static void Main(string[] args) { Publisher pub = new Publisher(); Subscriber sub1 = new Subscriber("sub1", pub); Subscriber sub2 = new Subscriber("sub2", pub); // Call the method that raises the event. pub.DoSomething(); // Keep the console window open Console.WriteLine("Press Enter to close this window."); Console.ReadLine(); } } }

Cmo: Producir eventos de una clase base en clases derivadas (Gua de programacin de C#)
El siguiente ejemplo simple muestra la forma estndar de declarar eventos en una clase base para que tambin se puedan generar desde clases derivadas. Este modelo se utiliza ampliamente en clases de formularios Windows Forms de la biblioteca de clases de .NET Framework. Cuando cree una clase que pueda utilizarse como clase base de otras clases, debe tener en cuenta que los eventos son un tipo especial de delegado que slo se pueden invocar desde dentro la clase que los declar. Las clases derivadas no pueden invocar directamente a eventos declarados dentro de la clase base. Aunque a veces puede ser conveniente que slo la clase base pueda provocar un evento, normalmente deber habilitar la clase derivada para invocar eventos de clase base. Para ello, puede crear un mtodo de invocacin protegido en la clase base que contiene el evento. Al llamar a este mtodo de invocacin o al reemplazarlo, las clases derivadas podrn invocar directamente el evento.

Nota:
No declare eventos virtuales en una clase base y los invalide en una clase derivada. El compilador de C# no controla estos eventos correctamente en Microsoft Visual Studio 2008 y no se puede prever si un suscriptor del evento derivado se estar suscribiendo realmente al evento de clase base.

Ejemplo

namespace BaseClassEvents {

using System; using System.Collections.Generic; // Special EventArgs class to hold info about Shapes. public class ShapeEventArgs : EventArgs { private double newArea; public ShapeEventArgs(double d) { newArea = d; } public double NewArea { get { return newArea; } } } // Base class event publisher public abstract class Shape { protected double area; public double Area { get { return area; } set { area = value; } } // The event. Note that by using the generic EventHandler<T> event type // we do not need to declare a separate delegate type. public event EventHandler<ShapeEventArgs> ShapeChanged; public abstract void Draw(); //The event-invoking method that derived classes can override. protected virtual void OnShapeChanged(ShapeEventArgs e) { // Make a temporary copy of the event to avoid possibility of // a race condition if the last subscriber unsubscribes // immediately after the null check and before the event is raised. EventHandler<ShapeEventArgs> handler = ShapeChanged; if (handler != null) { handler(this, e); } } } public class Circle : Shape { private double radius; public Circle(double d) { radius = d; area = 3.14 * radius; } public void Update(double d) { radius = d; area = 3.14 * radius;

OnShapeChanged(new ShapeEventArgs(area)); } protected override void OnShapeChanged(ShapeEventArgs e) { // Do any circle-specific processing here. // Call the base class event invocation method. base.OnShapeChanged(e); } public override void Draw() { Console.WriteLine("Drawing a circle"); } } public class Rectangle : Shape { private double length; private double width; public Rectangle(double length, double width) { this.length = length; this.width = width; area = length * width; } public void Update(double length, double width) { this.length = length; this.width = width; area = length * width; OnShapeChanged(new ShapeEventArgs(area)); } protected override void OnShapeChanged(ShapeEventArgs e) { // Do any rectangle-specific processing here. // Call the base class event invocation method. base.OnShapeChanged(e); } public override void Draw() { Console.WriteLine("Drawing a rectangle"); } } // Represents the surface on which the shapes are drawn // Subscribes to shape events so that it knows // when to redraw a shape. public class ShapeContainer { List<Shape> _list; public ShapeContainer() { _list = new List<Shape>(); } public void AddShape(Shape s) {

_list.Add(s); // Subscribe to the base class event. s.ShapeChanged += HandleShapeChanged; } // ...Other methods to draw, resize, etc. private void HandleShapeChanged(object sender, ShapeEventArgs e) { Shape s = (Shape)sender; // Diagnostic message for demonstration purposes. Console.WriteLine("Received event. Shape area is now {0}", e.NewArea); // Redraw the shape here. s.Draw(); } } class Test { static void Main(string[] args) { //Create the event publishers and subscriber Circle c1 = new Circle(54); Rectangle r1 = new Rectangle(12, 9); ShapeContainer sc = new ShapeContainer(); // Add the shapes to the container. sc.AddShape(c1); sc.AddShape(r1); // Cause some events to be raised. c1.Update(57); r1.Update(7, 7); // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } } } /* Output: Received event. Shape area is now 178.98 Drawing a circle Received event. Shape area is now 49 Drawing a rectangle */

Cmo: Implementar eventos de interfaz (Gua de programacin de C#)


Una interfaz puede declarar un evento. El ejemplo siguiente muestra cmo implementar eventos de interfaz en una clase. Bsicamente, las reglas son las mismas que cuando se implementa cualquier mtodo de interfaz o propiedad.

Para implementar eventos de interfaz en una clase

Declare el evento en la clase y, a continuacin, invquelo en las reas adecuadas.

Ejemplo

public interface IDrawingObject { event EventHandler ShapeChanged; } public class MyEventArgs : EventArgs {} public class Shape : IDrawingObject { event EventHandler ShapeChanged; void ChangeShape() { // Do something before the event OnShapeChanged(new MyEventsArgs()); // or do something after the event. } protected virtual void OnShapeChanged(MyEventArgs e) { if(ShapeChanged != null) { ShapeChanged(this, e); } } }

El ejemplo siguiente muestra cmo controlar la situacin menos comn en la que la clase se hereda de dos o ms interfaces y cada interfaz tiene un evento con el mismo nombre. En esta situacin, debe proporcionar una implementacin de interfaz explcita para al menos uno de los eventos. Cuando escriba una implementacin de interfaz explcita para un evento, tambin debe incluir los descriptores de acceso a eventos add y remove. Normalmente, estos descriptores los proporciona el compilador, pero en este caso no es as. Al proporcionar sus propios descriptores de acceso, puede especificar si los dos eventos se representan mediante el mismo evento en la clase o mediante eventos diferentes. Por ejemplo, si los eventos deben provocarse en momentos diferentes segn las especificaciones de la interfaz, luego puede asociar cada evento a una implementacin distinta en la clase. En el ejemplo siguiente, los suscriptores determinan qu evento OnDraw se recibir convirtiendo la referencia de la forma en IShape o IDrawingObject.

namespace WrapTwoInterfaceEvents { using System; public interface IDrawingObject { // Raise this event before drawing // the object. event EventHandler OnDraw; } public interface IShape { // Raise this event after drawing // the shape. event EventHandler OnDraw; }

// Base class event publisher inherits two // interfaces, each with an OnDraw event public class Shape : IDrawingObject, IShape { // Create an event for each interface event event EventHandler PreDrawEvent; event EventHandler PostDrawEvent; object objectLock = new Object(); // Explicit interface implementation required. // Associate IDrawingObject's event with // PreDrawEvent event EventHandler IDrawingObject.OnDraw { add { lock (objectLock) { PreDrawEvent += value; } } remove { lock (objectLock) { PreDrawEvent -= value; } } } // Explicit interface implementation required. // Associate IShape's event with // PostDrawEvent event EventHandler IShape.OnDraw { add { lock (objectLock) { PostDrawEvent += value; } } remove { lock (objectLock) { PostDrawEvent -= value; } }

} // For the sake of simplicity this one method // implements both interfaces. public void Draw() { // Raise IDrawingObject's event before the object is drawn.

EventHandler handler = PreDrawEvent; if (handler != null) { handler(this, new EventArgs()); } Console.WriteLine("Drawing a shape."); // RaiseIShape's event after the object is drawn. handler = PostDrawEvent; if (handler != null) { handler(this, new EventArgs()); } } } public class Subscriber1 { // References the shape object as an IDrawingObject public Subscriber1(Shape shape) { IDrawingObject d = (IDrawingObject)shape; d.OnDraw += new EventHandler(d_OnDraw); } void d_OnDraw(object sender, EventArgs e) { Console.WriteLine("Sub1 receives the IDrawingObject event."); } } // References the shape object as an IShape public class Subscriber2 { public Subscriber2(Shape shape) { IShape d = (IShape)shape; d.OnDraw += new EventHandler(d_OnDraw); } void d_OnDraw(object sender, EventArgs e) { Console.WriteLine("Sub2 receives the IShape event."); } }

public class Program { static void Main(string[] args) { Shape shape = new Shape(); Subscriber1 sub = new Subscriber1(shape); Subscriber2 sub2 = new Subscriber2(shape); shape.Draw(); // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } }

} /* Output: Sub1 receives the IDrawingObject event. Drawing a shape. Sub2 receives the IShape event. */

Sincronizacin de subprocesos (Gua de programacin de C#)


En las secciones siguientes se describen las caractersticas y clases que se pueden utilizar para sincronizar el acceso a recursos en aplicaciones multiproceso. Una de las ventajas de utilizar varios subprocesos en una aplicacin es que cada subproceso se ejecuta de forma asincrnica. En las aplicaciones para Windows, esto permite realizar las tareas que exigen mucho tiempo en segundo plano mientras la ventana de la aplicacin y los controles siguen respondiendo. En las aplicaciones de servidor, el subprocesamiento mltiple proporciona la capacidad de controlar cada solicitud de entrada con un subproceso diferente. De lo contrario, no se atendera cada nueva solicitud hasta que se hubiera satisfecho totalmente la solicitud anterior. Sin embargo, la naturaleza asincrnica de los subprocesos significa que el acceso a recursos como identificadores de archivos, conexiones de red y memoria se deben coordinar. De lo contrario, dos o ms subprocesos podran tener acceso al mismo tiempo al mismo recurso, cada uno desprevenido de las acciones del otro. El resultado seran daos imprevisibles en los datos. Para las operaciones simples en tipos de datos numricos enteros, la sincronizacin de subprocesos se puede lograr con miembros de la clase Interlocked. Para todos los dems tipos de datos y los recursos no seguros para subprocesos, el subprocesamiento mltiple slo se puede realizar sin ningn riesgo utilizando las estructuras de este tema. Para obtener informacin adicional sobre la programacin multiproceso, vea:

Utilizar el subprocesamiento (Gua de programacin de C#) Principios bsicos del subprocesamiento administrado Utilizar subprocesos y subprocesamiento Procedimientos recomendados para el subprocesamiento administrado

La palabra clave de bloqueo


La palabra clave lock se puede utilizar para garantizar que un bloque de cdigo se ejecuta hasta el final sin que lo interrumpan otros subprocesos. Esto se logra obteniendo un bloqueo de exclusin mutua para un objeto determinado durante la ejecucin de un bloque de cdigo. Una instruccin lock comienza con la palabra clave lock, que utiliza un objeto como argumento, seguida de un bloque de cdigo que slo un subproceso puede ejecutar a la vez. Por ejemplo:

public class TestThreading { private System.Object lockThis = new System.Object(); public void Function() { lock (lockThis) { // Access thread-sensitive resources. }

} }
El argumento suministrado a la palabra clave lock tiene que ser un objeto basado en un tipo de referencia y se utiliza para definir el mbito del bloqueo. En el ejemplo anterior, el mbito del bloqueo se limita a esta funcin porque no existe ninguna referencia al objeto lockThis fuera de la funcin. Si existiese una referencia de ese tipo, el mbito del bloqueo se extendera a ese objeto. Estrictamente, el objeto suministrado a lock slo se utiliza para identificar nicamente el recurso que varios subprocesos comparten, de modo que puede ser una instancia de clase arbitraria. Sin embargo, en la prctica, este objeto normalmente representa el recurso para el que la sincronizacin de subprocesos es necesaria. Por ejemplo, si varios subprocesos van a utilizar un objeto contenedor, se puede pasar el contenedor para bloquearlo. Entonces, el bloque de cdigo sincronizado que sigue al bloqueo tendra acceso al contenedor. Con tal de que otros subprocesos bloqueen el mismo contenedor antes de tener acceso a l, el acceso al objeto se sincroniza de forma segura. Generalmente, es mejor evitar el bloqueo en un tipo public o en instancias de objeto que estn fuera del control de la aplicacin. Por ejemplo, lock(this) puede ser problemtico si se puede tener acceso a la instancia pblicamente, ya que el cdigo que est fuera de su control tambin puede bloquear el objeto. Esto podra crear situaciones del interbloqueo, en las que dos o ms subprocesos esperan a que se libere el mismo objeto. El bloqueo de un tipo de datos pblico, como opuesto a un objeto, puede producir problemas por la misma razn. El bloqueo de cadenas literales es especialmente arriesgado porque el Common Language Runtime (CLR) interna las cadenas literales. Esto significa que hay una instancia de un literal de cadena determinado para todo el programa, exactamente el mismo objeto representa el literal en todos los dominios de la aplicacin en ejecucin, en todos los subprocesos. Como resultado, un bloqueo sobre una cadena que tiene el mismo contenido en cualquier parte del proceso de la aplicacin bloquea todas las instancias de esa cadena en la aplicacin. Por tanto, es mejor bloquear un miembro privado o protegido que no est internado. Algunas clases proporcionan especficamente los miembros para bloquear. Por ejemplo, el tipo Array proporciona SyncRoot. Muchos tipos de coleccin tambin proporcionan un miembro SyncRoot. Para obtener ms informacin sobre la palabra clave lock, vea:

lock (Instruccin, Referencia de C#) Cmo: Sincronizar un subproceso productor y un subproceso consumidor (Gua de programacin de C#)

Monitores
Al igual que la palabra clave lock, los monitores evitan que varios subprocesos ejecuten simultneamente bloques de cdigo. El mtodo Enter permite que un subproceso, y slo uno, continu con las instrucciones siguientes; todos los dems subprocesos se bloquean hasta que el subproceso en ejecucin llama a Exit. Esto es similar a utilizar la palabra clave lock. De hecho, la palabra clave lock se implementa con la clase Monitor. Por ejemplo:

lock (x) { DoSomething(); }

Esto equivale a:

System.Object obj = (System.Object)x; System.Threading.Monitor.Enter(obj); try { DoSomething(); } finally {

System.Threading.Monitor.Exit(obj); }

Normalmente, es preferible utilizar la palabra clave lock en vez de utilizar directamente la clase Monitor, porque lock es ms conciso y garantiza que se libera el monitor subyacente aunque el cdigo protegido produzca una excepcin. Esto se logra con la palabra clave finally, que ejecuta su bloque de cdigo asociado independientemente de que se produzca una excepcin. Para obtener ms informacin sobre los monitores, vea Ejemplo Monitor Synchronization Technology.

Eventos de sincronizacin y controladores de espera


El uso de un bloqueo o un monitor es til para evitar la ejecucin simultnea de bloques de cdigo utilizados por varios subprocesos, pero estas construcciones no permiten que un subproceso comunique un evento a otro. Esto requiere eventos de sincronizacin, que son objetos que tienen uno de dos estados (sealizado y no sealizado) y se pueden utilizar para activar y suspender subprocesos. Los subprocesos se pueden suspender haciendo que esperen a que se produzca un evento de sincronizacin que no est sealizado y se pueden activar cambiando el estado del evento a sealizado. Si un subproceso intenta esperar a que se produzca un evento que ya est sealizado, el subproceso se sigue ejecutando sin retraso. Hay dos tipos de eventos de sincronizacin: AutoResetEvent y ManualResetEvent. Slo difieren en que AutoResetEvent cambia automticamente de sealizado a no sealizado siempre que activa un subproceso. A la inversa, ManualResetEvent permite que cualquier nmero de subprocesos est activado si su estado es sealizado y slo vuelve al estado no sealizado cuando se llama a su mtodo Reset. Se puede hacer que los subprocesos esperen a que se produzcan eventos si se llama a uno de los mtodos de espera, como WaitOne, WaitAny o WaitAll. WaitHandle.WaitOne() hace que el subproceso espere hasta que se sealice un nico evento, WaitHandle.WaitAny() bloquea un subproceso hasta que se sealicen uno o varios eventos especificados y WaitHandle.WaitAll() bloquea el subproceso hasta que se sealicen todos los eventos indicados. Un evento se sealiza cuando se llama a su mtodo Set. En el ejemplo siguiente, la funcin Main crea e inicia un subproceso. El nuevo subproceso espera a que se produzca un evento mediante el mtodo WaitOne. Se suspende el subproceso hasta que el evento sea sealizado por el subproceso primario que est ejecutando la funcin Main. Cuando el evento se sealiza, vuelve a ejecutarse el subproceso auxiliar. En este caso, como el evento slo se utiliza para una activacin del subproceso, se podran utilizar las clases AutoResetEvent o ManualResetEvent.

using System; using System.Threading; class ThreadingExample { static AutoResetEvent autoEvent; static void DoWork() { Console.WriteLine(" autoEvent.WaitOne(); Console.WriteLine(" }

worker thread started, now waiting on event..."); worker thread reactivated, now exiting...");

static void Main() { autoEvent = new AutoResetEvent(false); Console.WriteLine("main thread starting worker thread...");

Thread t = new Thread(DoWork); t.Start(); Console.WriteLine("main thread sleeping for 1 second..."); Thread.Sleep(1000); Console.WriteLine("main thread signaling worker thread..."); autoEvent.Set(); } }

Para obtener ms ejemplos del uso de eventos de sincronizacin de subprocesos, vea:

Ejemplo Monitor Synchronization Technology Ejemplo Reader-Writer Synchronization Technology Ejemplo Thread Pools Technology Ejemplo Wait Synchronization Technology

Objeto Mutex
Una exclusin mutua es similar a un monitor; impide la ejecucin simultnea de un bloque de cdigo por ms de un subproceso a la vez. De hecho, el nombre "mutex" es una forma abreviada del trmino "mutuamente exclusivo". Sin embargo, a diferencia de los monitores, una exclusin mutua se puede utilizar para sincronizar los subprocesos entre varios procesos. Una exclusin mutua se representa mediante la clase Mutex. Cuando se utiliza para la sincronizacin entre procesos, una exclusin mutua se denomina una exclusin mutua con nombre porque va a utilizarla otra aplicacin y, por tanto, no se puede compartir por medio de una variable global o esttica. Se debe asignar un nombre para que ambas aplicaciones puedan tener acceso al mismo objeto de exclusin mutua. Aunque se puede utilizar una exclusin mutua para la sincronizacin de subprocesos dentro de un proceso, normalmente es preferible utilizar Monitor porque los monitores se disearon especficamente para .NET Framework y, por tanto, hacen un mejor uso de los recursos. Por el contrario, la clase Mutex es un contenedor para una construccin de Win32. Aunque es ms eficaz que un monitor, la exclusin mutua requiere transiciones de interoperabilidad, que utilizan ms recursos del sistema que la clase Monitor. Para obtener un ejemplo de uso de la exclusin mutua, vea Exclusiones mutuas (mutex).

Secciones relacionadas

Cmo: Crear y terminar subprocesos (Gua de programacin de C#) Cmo: Utilizar un grupo de subprocesos (Gua de programacin de C#) Cmo sincronizar el acceso a un recurso compartido en un entorno multiproceso mediante Visual C# HOW TO: Create a Thread by Using Visual C# .NET Cmo enviar un elemento de trabajo del grupo de subprocesos mediante Visual C# Cmo sincronizar el acceso a un recurso compartido en un entorno multiproceso mediante Visual C#

Cmo: Utilizar un diccionario para almacenar instancias de eventos (Gua de programacin de C#)
Uno de los usos de accessor-declarations consiste en exponer numerosos eventos sin tener que asignar un campo para cada uno, sino utilizando en su lugar un diccionario para almacenar las instancias de los eventos. Esto slo resulta til si se dispone de muchos eventos, pero se prev que la mayora de ellos no se implementarn.

Ejemplo
public delegate void EventHandler1(int i); public delegate void EventHandler2(string s); public class PropertyEventsSample { private System.Collections.Generic.Dictionary<string, System.Delegate> eventTable; public PropertyEventsSample() { eventTable = new System.Collections.Generic.Dictionary<string, System.Delegate>(); eventTable.Add("Event1", null); eventTable.Add("Event2", null); } public event EventHandler1 Event1 { add { lock (eventTable) { eventTable["Event1"] = (EventHandler1)eventTable["Event1"] + value; } } remove { lock (eventTable) { eventTable["Event1"] = (EventHandler1)eventTable["Event1"] - value; } } } public event EventHandler2 Event2 { add { lock (eventTable) { eventTable["Event2"] = (EventHandler2)eventTable["Event2"] + value; } } remove { lock (eventTable) { eventTable["Event2"] = (EventHandler2)eventTable["Event2"] - value;

} } } internal void RaiseEvent1(int i) { EventHandler1 handler1; if (null != (handler1 = (EventHandler1)eventTable["Event1"])) { handler1(i); } } internal void RaiseEvent2(string s) { EventHandler2 handler2; if (null != (handler2 = (EventHandler2)eventTable["Event2"])) { handler2(s); } } } public class TestClass { public static void Delegate1Method(int i) { System.Console.WriteLine(i); } public static void Delegate2Method(string s) { System.Console.WriteLine(s); } static void Main() { PropertyEventsSample p = new PropertyEventsSample(); p.Event1 += new EventHandler1(TestClass.Delegate1Method); p.Event1 += new EventHandler1(TestClass.Delegate1Method); p.Event1 -= new EventHandler1(TestClass.Delegate1Method); p.RaiseEvent1(2); p.Event2 += new EventHandler2(TestClass.Delegate2Method); p.Event2 += new EventHandler2(TestClass.Delegate2Method); p.Event2 -= new EventHandler2(TestClass.Delegate2Method); p.RaiseEvent2("TestString"); // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } } /* Output: 2 TestString */

Cmo: Implementar descriptores de acceso de eventos personalizados (Gua de programacin de C#)


Un evento es un tipo especial de delegado de multidifusin que slo puede invocarse desde dentro de la clase en la que se declara. El cdigo cliente se suscribe al evento proporcionando una referencia a un mtodo que debera invocarse cuando se desencadena el evento. Estos mtodos se agregan a la lista de invocacin del delegado a travs de descriptores de acceso de eventos, que son similares a los descriptores de acceso de propiedad, con la diferencia de que los descriptores de acceso de eventos se denominan add y remove. En la mayora de los casos, no tiene que proporcionar descriptores de acceso de eventos personalizados. Cuando no proporciona ningn descriptor de acceso de eventos personalizado en el cdigo, el compilador los agrega automticamente. Sin embargo, en algunos casos puede que tenga que proporcionar un comportamiento personalizado. Uno de estos casos se muestra en el tema Cmo: Implementar eventos de interfaz (Gua de programacin de C#).

Ejemplo
En el ejemplo siguiente se muestra cmo implementar descriptores de acceso de eventos add y remove personalizados. Aunque puede sustituir cualquier cdigo dentro de los descriptores de acceso, recomendamos que bloquee el evento antes de agregar o quitar un nuevo mtodo de control de eventos.

Copiar event EventHandler IDrawingObject.OnDraw { add { lock (PreDrawEvent) { PreDrawEvent += value; } } remove { lock (PreDrawEvent) { PreDrawEvent -= value; } } }

Ejemplo de eventos
En este ejemplo se explica cmo declarar, invocar y configurar eventos en C#. Para obtener ms informacin, consulte Eventos (Gua de programacin de C#).

Para obtener ejemplos e instrucciones para la instalacin

Siga uno o varios de los procedimientos siguientes: En el men Ayuda, haga clic en Ejemplos. El archivo Lame muestra informacin sobre los ejemplos.

Visite el sitio web Visual Studio 2008 Samples. Estn disponibles las versiones de ejemplos ms recientes.

Busque los ejemplos en el equipo en el que est instalado Visual Studio. De manera predeterminada, los ejemplos y el archivo Lame se instalan en unidad:\Archivos de programa\Microsoft Visual Studio 9.0\Samples\lcid. Para las versiones Express de Visual Studio, todos los ejemplos estn en lnea.

Para obtener ms informacin, vea Localizar archivos de ejemplo. .

Nota de seguridad:
En este ejemplo de cdigo se ilustra un concepto y nicamente se muestra el cdigo correspondiente a dicho concepto. Es posible que no cumpla los requisitos de seguridad de un entorno concreto y, por tanto, no debera utilizarse tal y como se muestra. Se recomienda agregar cdigo de seguridad y de control de errores para que los proyectos sean ms seguros y slidos. Microsoft proporciona este ejemplo "tal cual" sin ninguna garanta.

Para generar y ejecutar los ejemplos de eventos en Visual Studio:


1. 2. 3. En el Explorador de soluciones, haga clic con el botn secundario del mouse en el proyecto Events1 y, a continuacin, seleccione Establecer como proyecto de inicio. En el men Depurar, haga clic en Iniciar sin depurar. Repita los pasos anteriores para Events2.

Para generar y ejecutar los ejemplos de eventos desde un smbolo del sistema
1. 2. Utilice el comando Cambiar directorio para cambiar al directorio Events1. Escriba lo siguiente:

Copiar csc events1.cs events1


3. 4. Utilice el comando Cambiar directorio para cambiar al directorio Events2. Escriba lo siguiente:

Copiar csc events2.cs events2

Diseo de eventos
Los eventos son mecanismos que permiten al cdigo especfico de la aplicacin ejecutarse cuando se produce una accin. Los eventos se pueden producir antes de que ocurra la accin asociada (eventos anteriores) o despus de ella (eventos posteriores). Por ejemplo, cuando un usuario hace clic en un botn de una ventana, se inicia un evento posterior que permite que se ejecuten los mtodos especficos de la aplicacin. Un delegado del controlador de eventos se enlaza al mtodo que se va a ejecutar cuando el sistema inicia un evento. El controlador de eventos se agrega al evento para que pueda invocar a su mtodo cuando se provoca el evento. Eventos pueden tener datos especficos de eventos (por ejemplo, un evento de presionar el botn del mouse puede incluir datos sobre la ubicacin del cursor de la pantalla). La firma del mtodo de control de eventos es idntica a la firma del delegado del controlador de eventos. La firma del controlador de eventos sigue estas convenciones:

El tipo de valor devuelto es Void. El primer parmetro se denomina sender y es del tipo Object. ste es el objeto que provoc el evento. El segundo parmetro se denomina e y es de tipo EventArgs o una clase derivada de EventArgs. Estos son los datos especficos del evento. El mtodo toma exactamente dos parmetros.

Para obtener ms informacin sobre los eventos, vea Controlar y provocar eventos. Utilice System.EventHandler<T> en lugar de crear manualmente nuevos delegados que se van a utilizar como controladores de eventos. Esta instruccin se refiere principalmente a las reas de nuevas funciones. Si est expandiendo la funcionalidad en un rea que ya utiliza controladores de eventos no genricos, puede continuar utilizando controladores de eventos no genricos para mantener la coherencia del diseo. No puede cumplir esta instruccin si su biblioteca tiene como destino versiones de .NET Framework que no admiten eventos genricos. Considere la posibilidad de usar una clase derivada de System.EventArgs como argumento del evento, a menos que tenga la absoluta seguridad de que nunca ser necesario que el evento entregue ningn dato al mtodo de control de eventos, en cuyo caso puede utilizar directamente el tipo System.EventArgs. Si define un evento que toma una instancia de EventArgs en lugar de una clase derivada que defina usted, no podr agregar datos al evento en versiones posteriores. Por esa razn, es preferible crear una clase derivada vaca de EventArgs. Esto le permitir agregar datos al evento en versiones posteriores sin introducir cambios importantes. Utilice un mtodo virtual protegido para provocar cada evento. Esto slo es aplicable a los eventos no estticos de clases no selladas, no a estructuras, clases selladas ni eventos estticos. Al cumplir esta instruccin se permite a las clases derivadas controlar un evento de clase base reemplazando el mtodo protegido. El nombre del mtodo virtual protegido (Overridable en Visual Basic) debera ser igual que el nombre de evento que tiene el prefijo On. Por ejemplo, el mtodo virtual protegido para un evento llamado "TimeChanged" se denomina "OnTimeChanged".

Nota importante:
No se requiere que las clases derivadas que reemplazan el mtodo virtual protegido llamen a la implementacin de la clase base. La clase base debe continuar funcionando correctamente aun cuando no se llame a su implementacin.

Utilice un parmetro que tenga el mismo tipo que la clase de argumento de evento para el mtodo protegido que inicia un evento. El parmetro se debera denominar e. La clase FontDialog proporciona el mtodo siguiente, que provoca el evento Apply:

Este idioma no es compatible o no hay ningn ejemplo de cdigo disponible.


No pase null (Nothing en Visual Basic) como parmetro remitente (sender) al iniciar un evento no esttico. En eventos estticos, el parmetro sender debera ser null (Nothing en Visual Basic). No pase null (Nothing en Visual Basic) como parmetro de datos del evento al iniciar un evento. Si no hay ningn dato de eventos, pase Empty en lugar de null. Prevea la ejecucin arbitraria de cdigo en el mtodo de control de eventos. Considere la posibilidad de colocar el cdigo desde el que se provoca el evento en un bloque try-catch para impedir la finalizacin del programa a causa de excepciones no controladas iniciadas desde los controladores de eventos. Plantese iniciar eventos que pueda cancelar el usuario final. Esto slo es aplicable a los eventos anteriores. Si est diseando un evento cancelable, utilice CancelEventArgs en lugar de EventArgs como la clase base para el objeto de datos de eventos e. Portions Copyright 2005 Microsoft Corporation. Reservados todos los derechos. Portions Copyright Addison-Wesley Corporation. Reservados todos los derechos. Para obtener ms informacin sobre las directrices de diseo, consulte el libro titulado "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" de Krzysztof Cwalina y Brad Abrams, publicado por Addison-Wesley, 2005.

Controlar y provocar eventos


Los eventos de .NET Framework se basan en un modelo de delegado. Las personas familiarizadas con los patrones de diseo de la programacin orientada a objetos se darn cuenta del patrn de diseo de observador. Esta seccin contiene temas que describen el modelo de delegado, cmo consumir eventos en aplicaciones y cmo provocar los eventos de una clase. Para obtener informacin sobre la sintaxis de los eventos en un lenguaje de programacin especfico, vea la documentacin de ese lenguaje.

En esta seccin
Eventos y delegados Proporciona informacin general sobre el modelo de eventos y describe los delegados de .NET Framework. Cmo: Conectar mtodos controladores de eventos a eventos Muestra cmo definir un controlador de eventos y agregarlo a un evento iniciado en otra clase.

Consumir eventos Proporciona informacin general sobre cmo las aplicaciones de .NET Framework consumen eventos. Cmo: Consumir eventos en una aplicacin de Web Forms Muestra cmo controlar un evento iniciado por un control de formularios Web Forms. Cmo: Consumir eventos en una aplicacin de Windows Forms Muestra cmo controlar un evento iniciado por un control de Windows Forms. Provocar un evento Explica cmo incluir funcionalidad de eventos en una clase. Cmo: Implementar eventos en una clase Muestra cmo definir y provocar un evento en su clase. Cmo: Provocar y utilizar eventos Contiene un ejemplo detallado que provoca un evento en una clase y controla el evento en otra clase. Provocar mltiples eventos Describe una tcnica para optimizar el almacenamiento de varios eventos. Cmo: Controlar varios eventos con propiedades de eventos Muestra cmo utilizar propiedades de evento para controlar varios eventos.

Eventos y delegados
Un evento es un mensaje que enva un objeto cuando ocurre una accin. La accin puede estar causada por la interaccin del usuario, como un clic, o por otra lgica del programa. El objeto que provoca el evento se conoce como remitente del evento. El objeto que captura el evento y responde a l se denomina receptor del evento. En las comunicaciones de eventos, el remitente del evento no sabe qu objeto o mtodo recibir los eventos que provoca. Se necesita un intermediario (o mecanismo de tipo puntero) entre el origen y el receptor. .NET Framework define un tipo especial (Delegate) que proporciona la funcionalidad de un puntero a funcin. Un delegado es una clase que puede guardar una referencia a un mtodo. A diferencia de otras clases, una clase de delegado tiene un prototipo y puede guardar referencias nicamente a los mtodos que coinciden con su prototipo. Por lo tanto, un delegado equivale a un puntero a funcin con seguridad o a una devolucin de llamada. Aunque los delegados tienen otros usos, esta explicacin se centra en la funcionalidad de control de eventos de los delegados. Una declaracin de delegado es suficiente para definir una clase de delegado. La declaracin proporciona el prototipo del delegado y Common Language Runtime proporciona la implementacin. En el siguiente ejemplo se muestra la declaracin de un delegado de eventos.

VB C# C++ F# JScript

Copiar public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);

La sintaxis es similar a la de la declaracin de un mtodo; no obstante, la palabra clave delegate indica al compilador que AlarmEventHandler es un tipo de delegado. Por convencin, los delegados de evento de .NET Framework tienen dos parmetros, el origen que provoc el evento y los datos del evento. Una instancia del delegado AlarmEventHandler puede enlazarse a cualquier mtodo que coincida con su prototipo, como el mtodo AlarmRang de la clase WakeMeUp, como se muestra en el siguiente ejemplo.

VB C# C++ F# JScript

Copiar public class WakeMeUp { // AlarmRang has the same signature as AlarmEventHandler. public void AlarmRang(object sender, AlarmEventArgs e) {...}; ... }

Los delegados de evento personalizados slo son necesarios cuando un evento genera datos de evento. Muchos eventos, incluidos algunos eventos de interfaz de usuario, como los clics, no generan datos de evento. En estos casos, es apropiado el delegado proporcionado en la biblioteca de clases para el evento sin datos, System.EventHandler. A continuacin se muestra su declaracin.

delegate void EventHandler(object sender, EventArgs e);

Los delegados de evento son de multidifusin, lo que significa que pueden guardar referencias a ms de un mtodo de control de eventos. Para obtener informacin detallada, vea Delegate. Los delegados permiten realizar un control de eventos ms flexible y detallado. Un delegado acta como remitente de eventos de la clase que provoca el evento y mantiene una lista de los controladores registrados para el evento. Para obtener ms informacin sobre cmo usar delegados para incluir funcionalidad de eventos en un componente o control, vea Provocar un evento. Para obtener informacin general sobre cmo consumir eventos en sus aplicaciones, vea Consumir eventos.

Cmo: Conectar mtodos controlador de eventos a eventos


Para consumir eventos definidos en otra clase, debe definir y registrar un controlador de eventos. El controlador de eventos debe tener la misma firma de mtodo que el delegado declarado para el evento. Puede registrar su controlador de eventos agregando el controlador al evento. Una vez agregado, se llama al mtodo siempre que la clase provoca el evento. Para consultar un ejemplo completo de cmo provocar y controlar eventos, vea Cmo: Provocar y consumir eventos.

Para agregar un mtodo controlador de eventos para un evento


1. Defina un mtodo controlador de eventos con la misma firma que el delegado de eventos.

public class WakeMeUp { // AlarmRang has the same signature as AlarmEventHandler. public void AlarmRang(object sender, AlarmEventArgs e) {...}; ... }

Este idioma no es compatible o no hay ningn ejemplo de cdigo disponible.


2. Cree una instancia del delegado, utilizando una referencia al mtodo controlador de eventos. Cuando se llama a la instancia de delegado, sta, a su vez, llama al mtodo controlador de eventos.

// Create an instance of WakeMeUp. WakeMeUp w = new WakeMeUp();

// Instantiate the event delegate. AlarmEventHandler alhandler = new AlarmEventHandler(w.AlarmRang);

3.

Agregue la instancia de delegado al evento. Cuando se provoca el evento, se llama a la instancia de delegado y a su mtodo de controlador eventos asociado.

// Instantiate the event source. AlarmClock clock = new AlarmClock();

// Add the delegate instance to the event. clock.Alarm += alhandler;

Utilizar eventos

Para consumir un evento en una aplicacin, debe proporcionar un controlador de eventos (mtodo de control de eventos) que ejecute la lgica del programa en respuesta al evento, y que registre el controlador de eventos en el origen del evento. Este proceso se denomina conexin de eventos. Los diseadores visuales de formularios Windows Forms y formularios Web Forms disponen de herramientas para la programacin rpida de aplicaciones (RAD) que simplifican u ocultan los detalles de la conexin de eventos. En este tema se describe el modelo general de control de eventos. Para obtener informacin general sobre el modelo de eventos de .NET Framework, vea Eventos y delegados. Para obtener ms informacin sobre el modelo de eventos en formularios Windows Forms, vea Cmo: Consumir eventos en una aplicacin de formularios Windows Forms. Para obtener ms informacin sobre el modelo de eventos en formularios Web Forms, vea Cmo: Consumir eventos en una aplicacin de formularios Web Forms.

Patrn de eventos
Los detalles sobre el cableado de eventos son diferentes tanto en los formularios Windows Forms como en los formularios Web Forms debido a los niveles diferentes de compatibilidad proporcionados por diferentes herramientas RAD. Sin embargo, ambos escenarios siguen el mismo modelo de evento, que tiene las siguientes caractersticas:

Una clase que provoca un evento denominado EventName tiene el siguiente miembro:

public event EventNameEventHandler EventName; Este idioma no es compatible o no hay ningn ejemplo de cdigo disponible.
El delegado de eventos para el evento EventName es EventNameEventHandler, con la siguiente firma:

public delegate void EventNameEventHandler(object sender, EventNameEventArgs e); Este idioma no es compatible o no hay ningn ejemplo de cdigo disponible.
Para consumir el evento EventName, el controlador de eventos debe tener la misma firma que el delegado de eventos:

void EventHandler(object sender, EventNameEventArgs e) {}

Nota:
En .NET Framework, un delegado de eventos se denomina EventNameEventHandler, mientras que en la documentacin, controlador de eventos hace referencia a un mtodo de control de eventos. La razn de la utilizacin de este esquema de nombres es que un delegado EventNameEventHandler apunta al controlador de eventos (el mtodo) que en realidad controla el evento.

Cuando un evento no tiene ningn dato asociado, la clase que provoca el evento utiliza System.EventHandler como delegado y System.EventArgs para los datos del evento. Los eventos que tienen datos asociados utilizan clases que se derivan de EventArgs para ese tipo de datos de evento y el tipo de delegado de evento correspondiente. Por ejemplo, si se desea controlar un evento MouseUp en una aplicacin de Windows Forms, la clase de datos de evento es MouseEventArgs y el delegado de evento es MouseEventHandler. Observe que varios eventos de mouse utilizan una clase comn de datos de evento y un delegado de evento comn, por lo que el esquema de nombres no coincide exactamente con la convencin descrita anteriormente. En el caso de los eventos del mouse, el controlador de eventos debe tener la firma siguiente:

void Mouse_Moved(object sender, MouseEventArgs e){}

El remitente y los parmetros de argumentos de eventos proporcionan detalles adicionales sobre el evento del mouse al controlador de eventos. El objeto de remitente indica qu fue lo que provoc el evento. El parmetro MouseEventArgs proporciona detalles sobre el movimiento del mouse que provoc el evento. Muchos orgenes de eventos proporcionan datos adicionales para el evento y muchos controladores de eventos utilizan los datos especficos del evento para procesar el evento en cuestin. Para obtener un ejemplo que muestre cmo provocar y controlar eventos con datos especficos del evento, vea Cmo a: Provocar y utilizar eventos.

Nota:
Los eventos tambin se provocan fuera del contexto de las interfaces de usuario y, de hecho, .NET Framework incluye muchas clases que no son de interfaz de usuario y que provocan eventos. No obstante, todos los eventos siguen el patrn aqu descrito.

Para obtener informacin sobre cmo provocar los eventos de una clase, vea Provocar un evento.

Eventos estticos y dinmicos


.NET Framework permite a los suscriptores registrarse para la notificacin de eventos esttica o dinmicamente. Los controladores de eventos estticos son efectivos durante toda la vida de la clase cuyos eventos controlan. Este es el mtodo ms comn de controlar eventos. Los controladores de eventos dinmicos se activan y desactivan explcitamente durante la ejecucin de un programa, normalmente en respuesta a alguna lgica condicional del programa. Por ejemplo, pueden utilizarse si las notificaciones de eventos solo son necesarias en condiciones especficas o si una aplicacin proporciona varios controladores de eventos y las condiciones en tiempo de ejecucin determinan cul es el que debe utilizarse. El mtodo EventInfo.AddEventHandler agrega controladores de eventos dinmicos y el mtodo EventInfo.RemoveEventHandler los desactiva. Cada lenguaje proporciona tambin sus propias caractersticas para controlar eventos de forma dinmica. En el ejemplo siguiente se define una clase TemperatureMonitor que provoca un evento TemperatureTheshold siempre que la temperatura alcanza un umbral predefinido. Un controlador de eventos suscrito a este evento se activa y se desactiva durante la ejecucin del programa.

using System; public class TemperatureEventArgs : EventArgs { private decimal oldTemp; private decimal newTemp; public decimal OldTemperature { get { return this.oldTemp; } } public decimal NewTemperature { get { return this.newTemp; } } public TemperatureEventArgs(decimal oldTemp, decimal newTemp) { this.oldTemp = oldTemp; this.newTemp = newTemp; } }

public delegate void TemperatureEventHandler(object sender, TemperatureEventArgs ev); public class TemperatureMonitor { private decimal currentTemperature; private decimal threshholdTemperature; public event TemperatureEventHandler TemperatureThreshold; public TemperatureMonitor(decimal threshhold) { this.threshholdTemperature = threshhold; } public void SetTemperature(decimal newTemperature) { if ( (this.currentTemperature > this.threshholdTemperature && newTemperature <= this.threshholdTemperature) || (this.currentTemperature < this.threshholdTemperature && newTemperature >= this.threshholdTemperature) ) OnRaiseTemperatureEvent(newTemperature); this.currentTemperature = newTemperature; } public decimal GetTemperature() { return this.currentTemperature; } protected virtual void OnRaiseTemperatureEvent(decimal newTemperature) { // Raise the event if it has subscribers. if (TemperatureThreshold != null) TemperatureThreshold(this, new TemperatureEventArgs(this.currentTemperature, newTemperature)); } }

public class Example { public static void Main() { Example ex = new Example(); ex.MonitorTemperatures(); } public void MonitorTemperatures() { TemperatureMonitor tempMon = new TemperatureMonitor(32); tempMon.SetTemperature(33); Console.WriteLine("Current temperature is {0} degrees Fahrenheit.", tempMon.GetTemperature()); tempMon.SetTemperature(32); Console.WriteLine("Current temperature is {0} degrees Fahrenheit.", tempMon.GetTemperature()); // Add event handler dynamically using C# syntax. tempMon.TemperatureThreshold += this.TempMonitor;

tempMon.SetTemperature(33); Console.WriteLine("Current temperature is {0} degrees Fahrenheit.", tempMon.GetTemperature()); tempMon.SetTemperature(34); Console.WriteLine("Current temperature is {0} degrees Fahrenheit.", tempMon.GetTemperature()); tempMon.SetTemperature(32); Console.WriteLine("Current temperature is {0} degrees Fahrenheit.", tempMon.GetTemperature()); // Remove event handler dynamically using C# syntax. tempMon.TemperatureThreshold -= this.TempMonitor; tempMon.SetTemperature(31); Console.WriteLine("Current temperature is {0} degrees Fahrenheit.", tempMon.GetTemperature()); tempMon.SetTemperature(35); Console.WriteLine("Current temperature is {0} degrees Fahrenheit.", tempMon.GetTemperature()); } private void TempMonitor(object sender, TemperatureEventArgs e) { Console.WriteLine(" ***Warning: Temperature is changing from {0} to {1}.", e.OldTemperature, e.NewTemperature); } } // The example displays the following output: // Current temperature is 33 degrees Fahrenheit. // Current temperature is 32 degrees Fahrenheit. // Current temperature is 33 degrees Fahrenheit. // Current temperature is 34 degrees Fahrenheit. // ***Warning: Temperature is changing from 34 to 32. // Current temperature is 32 degrees Fahrenheit. // Current temperature is 31 degrees Fahrenheit. // Current temperature is 35 degrees Fahrenheit.

Cmo: Consumir eventos en una aplicacin de formularios Web Forms


Un escenario comn en las aplicaciones de formularios Web Forms es rellenar una pgina Web con controles y, a continuacin, realizar una accin concreta basada en el control en el que el usuario hace clic. Por ejemplo, un control System.Web.UI.WebControls.Button provoca un evento cuando el usuario hace clic en l en la pgina web. Controlando el evento, la aplicacin puede ejecutar la lgica de aplicacin adecuada para ese clic del botn. Para obtener informacin sobre el modelo de programacin de formularios Web Forms, vea Programar formularios Web Forms.

Para controlar un evento de clic de botn en una pgina Web


1. Cree una pgina de formularios Web Forms (pgina ASP.NET) que tenga un control Button.

Copiar <asp:Button id = "Button" Text = "Click Me" runat = server/>

2.

Defina un controlador de eventos que coincida con la firma de delegado del evento Click. El evento Click utiliza la clase EventHandler para el tipo de delegado y la clase EventArgs para los datos del evento.

void Button_Click(object sender, EventArgs e) {...}

3.

Establezca el atributo OnClick del elemento Button en el mtodo controlador de eventos.

Copiar <asp:Button id = "Button" OnClick = "Button_Click" Text = "Click Me" runat = server/>

Nota:
Un desarrollador de aplicaciones de formularios Web Forms puede conectar el evento mediante declaracin tal y como se muestra, sin trabajar directamente con el delegado. El marco de trabajo de la pgina ASP.NET genera un cdigo que crea una instancia de EventHandler que hace referencia a Button_Click y agrega esta instancia del delegado al evento Click de la instancia de Button.

Ejemplo

La siguiente pgina de formularios Web Forms controla el evento Click de Button para cambiar el color de fondo de TextBox. Los elementos en negrita que aparecen en este ejemplo muestran el cdigo del controlador de eventos y cmo se conecta al evento Click de Button.

Nota de seguridad:
En este ejemplo hay un cuadro de texto que acepta datos del usuario, lo que puede suponer una amenaza para la seguridad. De forma predeterminada, las pginas Web ASP.NET validan los datos escritos por el usuario para comprobar que no incluyen secuencias de comandos ni elementos HTML. Para obtener ms informacin, vea Informacin general sobre los ataques mediante secuencias de comandos.

<html> <script language="C#" runat=server>

private void Button_Click(object sender, EventArgs e){ Box.BackColor = System.Drawing .Color.LightGreen; } </script> <body> <form method="POST" action="Events.aspx" runat=server> Click the button, and notice the color of the text box.<br><br> <asp:TextBox id = "Box" Text = "Hello" BackColor = "Cyan" runat=server/> <br><br> <asp:Button id = "Button" OnClick = "Button_Click" Text = "Click Me" runat = server/> </form> </body> </html>

Cmo: Consumir eventos en una aplicacin de formularios Windows Forms


Un escenario comn en las aplicaciones de Windows Forms es mostrar un formulario con controles y, a continuacin, realizar una accin concreta basada en el control en el que el usuario hace clic. Por ejemplo, un control Button provoca un evento cuando el usuario hace clic en l en el formulario. Controlando el evento, la aplicacin puede ejecutar la lgica de aplicacin adecuada para ese clic del botn. Para obtener ms informacin sobre formularios Windows Forms, vea Introduccin a los formularios Windows Forms.

Para controlar un evento de clic de un botn en un formulario Windows Forms


1. Cree un formulario Windows Forms que tenga un control Button.

Copiar private Button button; Este idioma no es compatible o no hay ningn ejemplo de cdigo disponible.
2. Defina un controlador de eventos que coincida con la firma de delegado del evento Click. El evento Click utiliza la clase EventHandler para el tipo de delegado y la clase EventArgs para los datos del evento.

void Button_Click(object sender, EventArgs e) {...} Este idioma no es compatible o no hay ningn ejemplo de cdigo disponible.
3. Agregue el mtodo controlador de eventos al evento Click de Button.

button.Click += new EventHandler(this.Button_Click); Este idioma no es compatible o no hay ningn ejemplo de cdigo disponible.

Nota:

Un diseador (como Visual Studio 2005) realizar la conexin de este evento generando cdigo similar al cdigo de este ejemplo.

Ejemplo

En el siguiente ejemplo de cdigo se controla el evento Click de un control Button para cambiar el color de fondo de un control TextBox. Los elementos en negrita muestran el controlador de eventos y cmo se conecta al evento Click de la instancia de Button. El cdigo de este ejemplo se ha escrito sin utilizar un diseador visual (como Visual Studio 2005) y solamente contiene los elementos de programacin esenciales. Si utiliza un diseador, generar cdigo adicional.

using System; using System.ComponentModel; using System.Windows.Forms; using System.Drawing; public class MyForm : Form { private TextBox box; private Button button; public MyForm() : base() { box = new TextBox(); box.BackColor = System.Drawing.Color.Cyan; box.Size = new Size(100,100); box.Location = new Point(50,50); box.Text = "Hello"; button = new Button(); button.Location = new Point(50,100); button.Text = "Click Me"; // To wire the event, create // a delegate instance and add it to the Click event. button.Click += new EventHandler(this.Button_Click); Controls.Add(box); Controls.Add(button); } // The event handler. private void Button_Click(object sender, EventArgs e) { box.BackColor = System.Drawing.Color.Green; } // The STAThreadAttribute indicates that Windows Forms uses the // single-threaded apartment model. [STAThreadAttribute] public static void Main(string[] args) { Application.Run(new MyForm()); } } [Visual Basic] Copiar Option Strict On

Imports System.ComponentModel Imports System.Windows.Forms Imports System.Drawing Public Class MyForm Inherits Form Private box As TextBox Private WithEvents myButton As Button Public Sub New() box = New TextBox() box.BackColor = System.Drawing.Color.Cyan box.Size = New Size(100, 100) box.Location = New Point(50, 50) box.Text = "Hello" myButton = New Button() myButton.Location = New Point(50, 100) myButton.Text = "Click Me" AddHandler myButton.Click, AddressOf Me.Button_Click Controls.Add(box) Controls.Add(myButton) End Sub ' The event handler. Private Sub Button_Click(sender As Object, e As EventArgs) System.Drawing.Color.Green End Sub ' The STAThreadAttribute indicates that Windows Forms uses the ' single-threaded apartment model. <STAThreadAttribute()> _ Public Shared Sub Main(args() As String) Application.Run(New MyForm()) End Sub End Class Provocar un evento
Tres elementos interrelacionados proporcionan la funcionalidad de evento: una clase que proporciona los datos del evento, un delegado de evento y la clase que provoca el evento. .NET Framework tiene una convencin para asignar nombre a las clases y mtodos relacionados con los eventos. Si desea que la clase provoque un evento denominado EventName, necesita los siguientes elementos:

box.BackColor =

Una clase que guarde los datos del evento, denominada EventNameEventArgs. Esta clase debe derivarse de System.EventArgs. Un delegado para el evento, denominado EventNameEventHandler. Una clase que provoca el evento. Esta clase debe proporcionar la declaracin de evento (EventName) y un mtodo que provoca el evento (OnEventName).

La clase de datos del evento y la clase de delegado de evento ya deben estar definidas en la biblioteca de clases .NET Framework o en una biblioteca de clases de terceros. En este caso, no es necesario que defina las clases. Por ejemplo, si el evento no utiliza datos personalizados, puede utilizar System.EventArgs para los datos de evento y System.EventHandler para el delegado. Define un miembro de evento en la clase con la palabra clave event. Cuando el compilador encuentra una palabra clave event en la clase, crea un miembro privado como:

Copiar private EventNameHandler eh = null;


El compilador tambin crea los dos mtodos pblicos add_EventName y remove_EventName. Estos mtodos son enlaces de eventos que permiten combinar o quitar delegados del delegado de evento eh. El programador oculta los detalles.

Nota:
En lenguajes que no sean C# y Visual Basic 2005, puede que el compilador no genere automticamente el cdigo correspondiente a un miembro de evento, y que se tengan que definir explcitamente los enlaces de eventos y el campo del delegado privado.

Cuando haya definido su implementacin de evento, deber determinar cundo provocar el evento. El evento se provoca si se llama al mtodo protegido OnEventName en la clase que defini el evento o en una clase derivada. El mtodo OnEventName provoca el evento mediante la invocacin de los delegados, pasando todos los datos especficos de evento. Los mtodos delegados para el evento pueden realizar acciones para el evento o procesar los datos especficos de evento.

Nota:
El mtodo protegido OnEventName tambin permite que las clases derivadas reemplacen el evento sin asociarle un delegado. Una clase derivada siempre debe llamar al mtodo OnEventName de la clase base para asegurarse de que los delegados registrados reciben el evento.

Cuando desee controlar eventos iniciados en otra clase, agregue mtodos delegados al evento. Si no est familiarizado con el modelo de delegado de los eventos de .NET Framework, vea Eventos y delegados.

Cmo: Implementar eventos en una clase


Los siguientes procedimientos describen cmo implementar un evento en una clase. El primer procedimiento implementa un evento que no tiene datos asociados; utiliza las clases System.EventArgs y System.EventHandler para los datos de eventos y el controlador de delegados. El segundo procedimiento implementa un evento con datos personalizados; define las clases personalizadas para los datos de evento y el controlador del delegado de eventos. Para consultar un ejemplo completo de cmo provocar y controlar eventos, vea Cmo: Provocar y consumir eventos.

Para implementar un evento sin datos especficos del evento


1. Defina un miembro de evento pblico en la clase. Establezca el tipo del miembro de evento en un delegado System.EventHandler.

public class Countdown { ... public event EventHandler CountdownCompleted; }

Este idioma no es compatible o no hay ningn ejemplo de cdigo disponible.


2. Incluya en la clase un mtodo protegido que provoque el evento. D un nombre al mtodo OnEventName. Provoque el evento dentro del mtodo.

public class Countdown { ... public event EventHandler CountdownCompleted; protected virtual void OnCountdownCompleted(EventArgs e) { if (CountdownCompleted != null) CountdownCompleted(this, e); } }

3.

Determine cundo provocar el evento en la clase. Llame a OnEventName para provocar el evento.

public class Countdown { ... public void Decrement { internalCounter = internalCounter - 1; if (internalCounter == 0) OnCountdownCompleted(new EventArgs()); } }

Para implementar un evento con datos especficos del evento


1. Defina una clase que proporcione los datos del evento. D un nombre a la clase EventNameArgs, derive la clase de System.EventArgs y agregue los miembros especficos del evento.

public class AlarmEventArgs : EventArgs { private readonly int nrings = 0; private readonly bool snoozePressed = false; //Constructor. public AlarmEventArgs(bool snoozePressed, int nrings) { this.snoozePressed = snoozePressed; this.nrings = nrings; } //Properties. public string AlarmText { ... } public int NumRings { ... } public bool SnoozePressed{ ...

} }

2.

Declare un delegado para el evento. D un nombre al delegado EventNameEventHandler.

public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);

3.

Defina un miembro de evento pblico denominado EventName en la clase. Establezca el tipo del miembro de evento en el tipo de delegado de eventos.

public class AlarmClock { ... public event AlarmEventHandler Alarm; }

4.

Defina en la clase un mtodo protegido que provoque el evento. D un nombre al mtodo OnEventName. Provoque el evento dentro del mtodo.

public class AlarmClock { ... public event AlarmHandler Alarm; protected virtual void OnAlarm(AlarmEventArgs e) Alarm(this, e); } }

if (Alarm != null)

5.

Determine cundo provocar el evento en la clase. Llame a OnEventName para provocar el evento y pase los datos especficos del evento utilizando EventNameEventArgs.

Public Class AlarmClock { ... public void Start() { ... System.Threading.Thread.Sleep(300); AlarmEventArgs e = new AlarmEventArgs(false, 0); OnAlarm(e); } }

Cmo: Provocar y utilizar eventos


El siguiente programa de ejemplo muestra cmo producir un evento en una clase y cmo controlar dicho evento en otra clase. La clase AlarmClock define el evento Alarm pblico y proporciona los mtodos para provocar el evento. La clase AlarmEventArgs se deriva de EventArgs y define los datos especficos en un evento Alarm. La clase WakeMeUp define el mtodo AlarmRang, que

controla un evento Alarm. La clase AlarmDriver utiliza las clases juntas, estableciendo el mtodo AlarmRang de WakeMeUp para controlar el evento Alarm de AlarmClock. Este programa de ejemplo utiliza conceptos descritos de forma detallada en Eventos y delegados y Provocar un evento.

Ejemplo
// EventSample.cs. // namespace EventSample { using System; using System.ComponentModel; // Class that contains the data for // the alarm event. Derives from System.EventArgs. // public class AlarmEventArgs : EventArgs { private readonly bool snoozePressed ; private readonly int nrings; //Constructor. // public AlarmEventArgs(bool snoozePressed, int nrings) { this.snoozePressed = snoozePressed; this.nrings = nrings; } // The NumRings property returns the number of rings // that the alarm clock has sounded when the alarm event // is generated. // public int NumRings { get { return nrings;} } // The SnoozePressed property indicates whether the snooze // button is pressed on the alarm when the alarm event is generated. // public bool SnoozePressed { get {return snoozePressed;} } // The AlarmText property that contains the wake-up message. // public string AlarmText { get { if (snoozePressed) { return ("Wake Up!!! Snooze time is over."); } else

{ return ("Wake Up!"); } } } } // Delegate declaration. // public delegate void AlarmEventHandler(object sender, AlarmEventArgs e); // The Alarm class that raises the alarm event. // public class AlarmClock { private bool snoozePressed = false; private int nrings = 0; private bool stop = false; // The Stop property indicates whether the // alarm should be turned off. // public bool Stop { get {return stop;} set {stop = value;} } // The SnoozePressed property indicates whether the snooze // button is pressed on the alarm when the alarm event is generated. // public bool SnoozePressed { get {return snoozePressed;} set {snoozePressed = value;} } // The event member that is of type AlarmEventHandler. // public event AlarmEventHandler Alarm; // The protected OnAlarm method raises the event by invoking // the delegates. The sender is always this, the current instance // of the class. // protected virtual void OnAlarm(AlarmEventArgs e) { AlarmEventHandler handler = Alarm; if (handler != null) { // Invokes the delegates. handler(this, e); } } // // // // // // This alarm clock does not have a user interface. To simulate the alarm mechanism it has a loop that raises the alarm event at every iteration with a time delay of 300 milliseconds, if snooze is not pressed. If snooze is pressed,

// the time delay is 1000 milliseconds. // public void Start() { for (;;) { nrings++; if (stop) { break; } else if (snoozePressed) { System.Threading.Thread.Sleep(1000); { AlarmEventArgs e = new AlarmEventArgs(snoozePressed, nrings); OnAlarm(e); } } else { System.Threading.Thread.Sleep(300); AlarmEventArgs e = new AlarmEventArgs(snoozePressed, nrings); OnAlarm(e); } } } } // The WakeMeUp class has a method AlarmRang that handles the // alarm event. // public class WakeMeUp { public void AlarmRang(object sender, AlarmEventArgs e) { Console.WriteLine(e.AlarmText +"\n"); if (!(e.SnoozePressed)) { if (e.NumRings % 10 == 0) { Console.WriteLine(" Let alarm ring? Enter Y"); Console.WriteLine(" Press Snooze? Enter N"); Console.WriteLine(" Stop Alarm? Enter Q"); String input = Console.ReadLine(); if (input.Equals("Y") ||input.Equals("y")) return; else if (input.Equals("N") || input.Equals("n")) { ((AlarmClock)sender).SnoozePressed = true; return; } else

{ ((AlarmClock)sender).Stop = true; return; } } } else { Console.WriteLine(" Let alarm ring? Enter Y"); Console.WriteLine(" Stop Alarm? Enter Q"); String input = Console.ReadLine(); if (input.Equals("Y") || input.Equals("y")) return; else { ((AlarmClock)sender).Stop = true; return; } } } }

// The driver class that hooks up the event handling method of // WakeMeUp to the alarm event of an Alarm object using a delegate. // In a forms-based application, the driver class is the // form. // public class AlarmDriver { public static void Main (string[] args) { // Instantiates the event receiver. WakeMeUp w= new WakeMeUp(); // Instantiates the event source. AlarmClock clock = new AlarmClock(); // Wires the AlarmRang method to the Alarm event. clock.Alarm += new AlarmEventHandler(w.AlarmRang); clock.Start(); } } }

Provocar mltiples eventos


Si la clase provoca varios eventos y se programan como se describe en Provocar un evento, el compilador generar un campo por cada instancia del delegado de evento. Si el nmero de eventos es alto, es posible que el costo de almacenamiento de un campo por delegado no sea aceptable. Para estos casos, .NET Framework dispone de una construccin que se conoce como propiedades de evento (eventos personalizados en Visual Basic 2005) y que se puede utilizar con otras estructuras de datos (de eleccin propia) para almacenar los delegados de evento. Las propiedades Event estn compuestas de declaraciones de evento acompaadas de descriptores de acceso de evento. Los descriptores de acceso de eventos son mtodos que se definen para poder agregar o quitar instancias del delegado de evento de la estructura de datos de almacenamiento. Hay que tener en cuenta que las propiedades de evento son ms lentas que los

campos de evento, ya que se debe obtener cada delegado de evento antes de poder invocarlo. La memoria y la velocidad se ven afectadas. Si la clase define muchos eventos que no se provocan con frecuencia, es posible que desee implementar propiedades de evento. Los controles de formularios Windows Forms y los controles de servidor ASP.NET utilizan propiedades de evento en lugar de campos de evento.

Vea tambin

Tareas Cmo: Controlar varios eventos mediante propiedades de eventos Cmo: Declarar eventos que evitan que se pierda memoria

Cmo: Controlar varios eventos mediante propiedades de eventos


Para poder utilizar propiedades de evento (eventos personalizados de Visual Basic 2005), hay que definirlas en la clase que provoca los eventos y, a continuacin, establecer los delegados de las propiedades de evento en las clases que controlan los eventos. Para implementar varias propiedades de evento en una clase, esta clase deber almacenar internamente y mantener el delegado definido para cada evento. Para hacerlo, uno de los enfoques tpicos consiste en implementar una coleccin de delegados que se indice por medio de una clave de evento. Para almacenar los delegados de cada evento, puede utilizar la clase EventHandlerList o implementar su propia coleccin. La clase de coleccin debe proporcionar mtodos para establecer, obtener acceso y recuperar el delegado del controlador de eventos basndose en la clave del evento. Por ejemplo, se puede utilizar una clase Hashtable o derivar una clase personalizada a partir de la clase DictionaryBase. No es necesario exponer los detalles de implementacin de la coleccin de delegados fuera de la clase. Cada una de las propiedades de evento de la clase define un mtodo de descriptor de acceso add y un mtodo de descriptor de acceso remove. El descriptor de acceso add de una propiedad de evento agrega la instancia de delegado de entrada a la coleccin de delegados. El descriptor de acceso remove de una propiedad de evento elimina la instancia de delegado de entrada de la coleccin de delegados. Los descriptores de acceso de las propiedades de eventos utilizan la clave predefinida de la propiedad de evento para agregar y eliminar instancias en la coleccin de delegados.

Para controlar varios eventos mediante las propiedades de evento


1. 2. 3. 4. 5. Defina una coleccin de delegados dentro de la clase que provoca los eventos. Defina una clave para cada evento. Defina las propiedades de evento de la clase que provoca los eventos. Utilice la coleccin de delegados para implementar los mtodos de descriptor de acceso add y remove de las propiedades de evento. Utilice las propiedades de evento pblicas para agregar y quitar delegados de controlador de eventos en las clases que controlan los eventos.

Ejemplo

En el siguiente ejemplo de C#, se implementan las propiedades de evento MouseDown y MouseUp mediante el uso de EventHandlerList para almacenar el delegado de cada evento. Las palabras clave de las construcciones de propiedades de evento estn en negrita.

Nota:
Estas propiedades no se admiten en Visual Basic 2005.

// The class SampleControl defines two event properties, MouseUp and MouseDown. class // // // SampleControl: Component { : Define other control methods and properties. :

// Define the delegate collection. protected EventHandlerList listEventDelegates = new EventHandlerList(); // Define a unique key for each event. static readonly object mouseDownEventKey = new object(); static readonly object mouseUpEventKey = new object(); // Define the MouseDown event property. public event MouseEventHandler MouseDown { // Add the input delegate to the collection. add { listEventDelegates.AddHandler(mouseDownEventKey, value); } // Remove the input delegate from the collection. remove { listEventDelegates.RemoveHandler(mouseDownEventKey, value); } } // Define the MouseUp event property. public event MouseEventHandler MouseUp { // Add the input delegate to the collection. add { listEventDelegates.AddHandler(mouseUpEventKey, value); } // Remove the input delegate from the collection. remove { listEventDelegates.RemoveHandler(mouseUpEventKey, value); } } }

Cmo: Declarar eventos que evitan que se pierda memoria


Hay varias circunstancias en las que es importante que una aplicacin mantenga un bajo uso de memoria. Los eventos personalizados permiten a la aplicacin utilizar memoria slo para los eventos que controla. De manera predeterminada, cuando una clase declara un evento, el compilador asigna memoria para que un campo almacene informacin de eventos. Si una clase tiene muchos eventos no usados, ocupan memoria intilmente. En lugar de utilizar la implementacin predeterminada de los eventos que Visual Basic proporciona, puede utilizar los eventos personalizados para administrar ms cuidadosamente el uso de memoria.

Ejemplo
En este ejemplo, la clase utiliza una instancia de la clase EventHandlerList, almacenada en el campo Events, para guardar informacin sobre los eventos que estn en uso. La clase EventHandlerList es una clase de lista optimizada diseada para contener delegados. Todos los eventos de la clase usan el campo Events para realizar un seguimiento de qu mtodos est controlando cada evento.

Cmo: Controlar varios eventos mediante propiedades de eventos


Para poder utilizar propiedades de evento (eventos personalizados de Visual Basic 2005), hay que definirlas en la clase que provoca los eventos y, a continuacin, establecer los delegados de las propiedades de evento en las clases que controlan los eventos. Para implementar varias propiedades de evento en una clase, esta clase deber almacenar internamente y mantener el delegado definido para cada evento. Para hacerlo, uno de los enfoques tpicos consiste en implementar una coleccin de delegados que se indice por medio de una clave de evento. Para almacenar los delegados de cada evento, puede utilizar la clase EventHandlerList o implementar su propia coleccin. La clase de coleccin debe proporcionar mtodos para establecer, obtener acceso y recuperar el delegado del controlador de eventos basndose en la clave del evento. Por ejemplo, se puede utilizar una clase Hashtable o derivar una clase personalizada a partir de la clase DictionaryBase. No es necesario exponer los detalles de implementacin de la coleccin de delegados fuera de la clase. Cada una de las propiedades de evento de la clase define un mtodo de descriptor de acceso add y un mtodo de descriptor de acceso remove. El descriptor de acceso add de una propiedad de evento agrega la instancia de delegado de entrada a la coleccin de delegados. El descriptor de acceso remove de una propiedad de evento elimina la instancia de delegado de entrada de la coleccin de delegados. Los descriptores de acceso de las propiedades de eventos utilizan la clave predefinida de la propiedad de evento para agregar y eliminar instancias en la coleccin de delegados.

Para controlar varios eventos mediante las propiedades de evento


1. 2. 3. 4. 5. Defina una coleccin de delegados dentro de la clase que provoca los eventos. Defina una clave para cada evento. Defina las propiedades de evento de la clase que provoca los eventos. Utilice la coleccin de delegados para implementar los mtodos de descriptor de acceso add y remove de las propiedades de evento. Utilice las propiedades de evento pblicas para agregar y quitar delegados de controlador de eventos en las clases que controlan los eventos.

Ejemplo

En el siguiente ejemplo de C#, se implementan las propiedades de evento MouseDown y MouseUp mediante el uso de EventHandlerList para almacenar el delegado de cada evento. Las palabras clave de las construcciones de propiedades de evento estn en negrita.

Nota:
Estas propiedades no se admiten en Visual Basic 2005.

// The class SampleControl defines two event properties, MouseUp and MouseDown. class // // // SampleControl: Component { : Define other control methods and properties. :

// Define the delegate collection. protected EventHandlerList listEventDelegates = new EventHandlerList(); // Define a unique key for each event. static readonly object mouseDownEventKey = new object();

static readonly object mouseUpEventKey = new object(); // Define the MouseDown event property. public event MouseEventHandler MouseDown { // Add the input delegate to the collection. add { listEventDelegates.AddHandler(mouseDownEventKey, value); } // Remove the input delegate from the collection. remove { listEventDelegates.RemoveHandler(mouseDownEventKey, value); } } // Define the MouseUp event property. public event MouseEventHandler MouseUp { // Add the input delegate to the collection. add { listEventDelegates.AddHandler(mouseUpEventKey, value); } // Remove the input delegate from the collection. remove { listEventDelegates.RemoveHandler(mouseUpEventKey, value); } } }

You might also like