Entiendo el propósito de los eventos, especialmente en el contexto de la creación de interfaces de usuario. Creo que este es el prototipo para crear un evento:
public void EventName(object sender, EventArgs e);
¿Qué hacen los controladores de eventos, por qué son necesarios y cómo puedo crear uno?
c#
.net
events
event-handling
Levi Campbell
fuente
fuente
Respuestas:
Para comprender los controladores de eventos, debe comprender a los delegados . En C # , puede pensar en un delegado como un puntero (o una referencia) a un método. Esto es útil porque el puntero se puede pasar como un valor.
El concepto central de un delegado es su firma o forma. Es decir (1) el tipo de retorno y (2) los argumentos de entrada. Por ejemplo, si creamos un delegado
void MyDelegate(object sender, EventArgs e)
, solo puede apuntar a métodos que regresanvoid
, y tomar unobject
yEventArgs
. Como un agujero cuadrado y una clavija cuadrada. Entonces, decimos que estos métodos tienen la misma firma o forma que el delegado.Entonces, sabiendo cómo crear una referencia a un método, pensemos en el propósito de los eventos: queremos hacer que se ejecute algún código cuando algo sucede en otra parte del sistema, o "manejar el evento". Para hacer esto, creamos métodos específicos para el código que queremos que se ejecute. El pegamento entre el evento y los métodos a ejecutar son los delegados. El evento debe almacenar internamente una "lista" de punteros a los métodos para llamar cuando se genera el evento. * Por supuesto, para poder llamar a un método, ¡necesitamos saber qué argumentos pasarle! Usamos al delegado como el "contrato" entre el evento y todos los métodos específicos que se llamarán.
Entonces, el valor predeterminado
EventHandler
(y muchos similares) representa una forma específica del método (nuevamente, void / object-EventArgs). Cuando declaras un evento, estás diciendo qué forma de método (EventHandler) invocará ese evento, al especificar un delegado:(* Esta es la clave de los eventos en .NET y elimina la "magia": un evento es, en realidad, solo una lista de métodos de la misma "forma". La lista se almacena donde vive el evento. el evento se "genera", en realidad es solo "ir a través de esta lista de métodos y llamar a cada uno, utilizando estos valores como parámetros". Asignar un controlador de eventos es solo una forma más bonita y fácil de agregar su método a esta lista de métodos ser llamado).
fuente
C # conoce dos términos,
delegate
yevent
. Comencemos con el primero.Delegar
A
delegate
es una referencia a un método. Al igual que puede crear una referencia a una instancia:Puede usar un delegado para crear una referencia a un método:
Ahora que tiene esta referencia a un método, puede llamar al método a través de la referencia:
¿Pero por qué lo harías? También puedes llamar
myFactory.GetInstance()
directamente. En este caso puedes. Sin embargo, hay muchos casos para pensar en los que no desea que el resto de la aplicación tenga conocimientomyFactory
o llamemyFactory.GetInstance()
directamente.Una obvia es si usted quiere ser capaz de reemplazar
myFactory.GetInstance()
amyOfflineFakeFactory.GetInstance()
partir de un lugar central (aka Factory Method ).Patrón de método de fábrica
Entonces, si tiene una
TheOtherClass
clase y necesita usarlamyFactory.GetInstance()
, así es como se verá el código sin delegados (deberá informarTheOtherClass
sobre el tipo de sumyFactory
):Si usaría delegados, no tiene que exponer el tipo de mi fábrica:
Por lo tanto, puede asignar un delegado a otra clase para que lo use, sin exponer su tipo a ellos. Lo único que está exponiendo es la firma de su método (cuántos parámetros tiene y tal).
"Firma de mi método", ¿dónde escuché eso antes? ¡Oh sí, interfaces! Las interfaces describen la firma de toda una clase. ¡Piense en los delegados como describiendo la firma de un solo método!
Otra gran diferencia entre una interfaz y un delegado es que cuando escribe su clase, no tiene que decirle a C # "este método implementa ese tipo de delegado". Con las interfaces, debe decir "esta clase implementa ese tipo de interfaz".
Además, una referencia de delegado puede (con algunas restricciones, ver más abajo) hacer referencia a múltiples métodos (llamados
MulticastDelegate
). Esto significa que cuando llama al delegado, se ejecutarán varios métodos adjuntos explícitamente. Una referencia de objeto siempre puede hacer referencia a un solo objeto.Las restricciones para a
MulticastDelegate
son que la firma (método / delegado) no debe tener ningún valor de retorno (void
) y las palabras claveout
yref
no se utiliza en la firma. Obviamente, no puede llamar a dos métodos que devuelven un número y esperan que devuelvan el mismo número. Una vez que la firma cumple, el delegado es automáticamente aMulticastDelegate
.Evento
Los eventos son solo propiedades (como get; set; propiedades para campos de instancia) que exponen la suscripción al delegado de otros objetos. Estas propiedades, sin embargo, no admiten get; set ;. En cambio, admiten agregar; eliminar;
Entonces puedes tener:
Uso en la interfaz de usuario (WinForms, WPF, UWP, etc.)
Entonces, ahora sabemos que un delegado es una referencia a un método y que podemos tener un evento para que el mundo sepa que pueden darnos sus métodos para que nuestro delegado nos haga referencia, y somos un botón de interfaz de usuario, entonces: puede preguntar a cualquier persona interesada en si se me hizo clic, para registrar su método con nosotros (a través del evento que expusimos). Podemos usar todos los métodos que nos fueron proporcionados y hacer referencia a ellos por nuestro delegado. Y luego, esperaremos y esperaremos ... hasta que aparezca un usuario y haga clic en ese botón, entonces tendremos suficientes razones para invocar al delegado. Y debido a que el delegado hace referencia a todos los métodos que nos han dado, se invocarán todos esos métodos. No sabemos qué hacen esos métodos, ni sabemos qué clase implementa esos métodos. Lo único que nos importa es que alguien esté interesado en que se haga clic en nosotros,
Java
Lenguajes como Java no tienen delegados. Usan interfaces en su lugar. La forma en que lo hacen es pedirle a cualquiera que esté interesado en 'que se nos haga clic', que implemente una determinada interfaz (con un cierto método al que podamos llamar), y luego darnos la instancia completa que implementa la interfaz. Mantenemos una lista de todos los objetos que implementan esta interfaz y podemos llamar a su 'cierto método que podemos llamar' cada vez que hacemos clic.
fuente
event
es una simple sintaxis, nada más.Esa es en realidad la declaración de un controlador de eventos, un método que se llamará cuando se active un evento. Para crear un evento, escribirías algo como esto:
Y luego puedes suscribirte al evento así:
Con OnMyEvent () definido así:
Siempre que se
Foo
dispareMyEvent
, seOnMyEvent
llamará a su controlador.No siempre tiene que usar una instancia de
EventArgs
como segundo parámetro. Si desea incluir información adicional, puede usar una clase derivada deEventArgs
(EventArgs
es la base por convención). Por ejemplo, si observa algunos de los eventos definidosControl
en WinForms oFrameworkElement
en WPF, puede ver ejemplos de eventos que pasan información adicional a los controladores de eventos.fuente
OnXXX
patrón de nombres para su controlador de eventos. (Estúpidamente, se considera que OnXXX significa 'manejar XXX' en MFC y 'elevar XXX' en .net, por lo que ahora su significado no es claro y confuso; consulte esta publicación para obtener más detalles ). Los nombres preferidos seríanRaiseXXX
para generar eventos y /HandleXXX
oSender_XXX
para controladores de eventos.Aquí hay un ejemplo de código que puede ayudar:
fuente
OnMaximum?.Invoke(this,new MyEventArgs("you've entered..."));
Solo para agregar a las excelentes respuestas existentes aquí: construir sobre el código en el aceptado, que utiliza un
delegate void MyEventHandler(string foo)
...Como el compilador conoce el tipo de delegado del evento SomethingHappened , esto:
Es totalmente equivalente a:
Y manipuladores también puede ser registrado con
-=
la siguiente manera:En aras de la exhaustividad, el aumento del evento se puede hacer de esta manera, solo en la clase propietaria del evento:
Se necesita la copia local del subproceso del controlador para asegurarse de que la invocación es segura para el subproceso; de lo contrario, un subproceso podría ir y anular el registro del último controlador para el evento inmediatamente después de comprobar si era así
null
, y nos divertiríamosNullReferenceException
existe .C # 6 introdujo una bonita mano corta para este patrón. Utiliza el operador de propagación nulo.
fuente
Mi comprensión de los eventos es;
Delegar:
Una variable para contener la referencia al método / métodos a ejecutar. Esto hace posible pasar métodos como una variable.
Pasos para crear y llamar al evento:
El evento es una instancia de un delegado.
Como un evento es una instancia de un delegado, primero tenemos que definir el delegado.
Asigne el método / métodos que se ejecutarán cuando se active el evento ( Llamar al delegado )
Dispara el evento ( llama al delegado )
Ejemplo:
fuente
editor: donde ocurren los eventos. El editor debe especificar qué delegado está usando la clase y generar los argumentos necesarios, pasar esos argumentos y a sí mismo al delegado.
suscriptor: donde ocurre la respuesta. El suscriptor debe especificar métodos para responder a los eventos. Estos métodos deben tomar el mismo tipo de argumentos que el delegado. El suscriptor luego agrega este método al delegado del editor.
Por lo tanto, cuando el evento ocurra en el editor, el delegado recibirá algunos argumentos del evento (datos, etc.), pero el editor no tiene idea de lo que sucederá con todos estos datos. Los suscriptores pueden crear métodos en su propia clase para responder a eventos en la clase del editor, de modo que los suscriptores puedan responder a los eventos del editor.
fuente
fuente
Estoy de acuerdo con KE50, excepto que veo la palabra clave 'evento' como un alias para 'ActionCollection' ya que el evento contiene una colección de acciones a realizar (es decir, el delegado).
fuente
Grandes respuestas técnicas en el post! Técnicamente no tengo nada que agregar a eso.
¡Una de las razones principales por las que aparecen nuevas funciones en los idiomas y el software en general es el marketing o la política de la empresa! :-) ¡Esto no debe ser subestimado!
¡Creo que esto también se aplica a ciertos delegados y eventos! Los encuentro útiles y agrego valor al lenguaje C #, pero por otro lado, ¡el lenguaje Java decidió no usarlos! decidieron que cualquier cosa que esté resolviendo con delegados ya puede resolver con las características existentes del lenguaje, es decir, interfaces, por ejemplo
Ahora, alrededor de 2001, Microsoft lanzó el .NET Framework y el lenguaje C # como una solución de la competencia para Java, por lo que fue bueno tener NUEVAS CARACTERÍSTICAS que Java no tiene.
fuente
Recientemente hice un ejemplo de cómo usar eventos en C # y lo publiqué en mi blog. Traté de hacerlo lo más claro posible, con un ejemplo muy simple. En caso de que pueda ayudar a alguien, aquí está: http://www.konsfik.com/using-events-in-csharp/
Incluye la descripción y el código fuente (con muchos comentarios), y se centra principalmente en el uso adecuado (como plantilla) de eventos y controladores de eventos.
Algunos puntos clave son:
Los eventos son como "subtipos de delegados", solo que más restringidos (en el buen sentido). De hecho, la declaración de un evento siempre incluye un delegado (los EventHandlers son un tipo de delegado).
Los controladores de eventos son tipos específicos de delegados (puede pensar en ellos como una plantilla), que obligan al usuario a crear eventos que tienen una "firma" específica. La firma tiene el formato: (remitente del objeto, EventArgs eventarguments).
Puede crear su propia subclase de EventArgs para incluir cualquier tipo de información que el evento necesite transmitir. No es necesario usar EventHandlers cuando se usan eventos. Puede omitirlos por completo y usar su propio tipo de delegado en su lugar.
Una diferencia clave entre el uso de eventos y delegados es que los eventos solo se pueden invocar desde la clase en la que se declararon, aunque se puedan declarar como públicos. Esta es una distinción muy importante, ya que permite que sus eventos estén expuestos para que estén "conectados" a métodos externos, mientras que al mismo tiempo están protegidos del "mal uso externo".
fuente
Otra cosa que debe saber , en algunos casos, debe usar los Delegados / Eventos cuando necesita un bajo nivel de acoplamiento !
Si desea usar un componente en varios lugares de la aplicación , debe hacer un componente con un bajo nivel de acoplamiento y la LÓGICA específica no importa debe delegarse FUERA de su componente. Esto garantiza que tenga un sistema desacoplado y un código más limpio.
En principio SÓLIDO esta es la " D ", ( D principio de inversión de dependencia ).
También conocido como " IoC ", Inversión de control .
Puede hacer " IoC " con Eventos, Delegados y DI (Inyección de dependencia).
Es fácil acceder a un método en una clase secundaria. Pero es más difícil acceder a un método en una clase padre desde niño. ¡Tienes que pasar la referencia del padre al hijo! (o use DI con interfaz)
¡Delegados / Eventos nos permite comunicarnos del niño al padre sin referencia!
En este diagrama anterior, no utilizo Delegado / Evento y el componente primario B debe tener una referencia del componente primario A para ejecutar la lógica de negocios no preocupada en el método de A. (alto nivel de acoplamiento)
¡Con este enfoque, tendría que poner todas las referencias de todos los componentes que usan el componente B! :(
En este diagrama anterior, uso Delegado / Evento y el componente B no tiene que conocer A. (bajo nivel de acoplamiento)
¡Y puede usar su componente B en cualquier lugar de su aplicación !
fuente