que - tipos de eventos c#
Entendiendo eventos y manejadores de eventos en C# (9)
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 manejadores de eventos, por qué son necesarios y cómo puedo crear uno?
Aquí hay un ejemplo de código que puede ayudar:
using System;
using System.Collections.Generic;
using System.Text;
namespace Event_Example
{
// First we have to define a delegate that acts as a signature for the
// function that is ultimately called when the event is triggered.
// You will notice that the second parameter is of MyEventArgs type.
// This object will contain information about the triggered event.
public delegate void MyEventHandler(object source, MyEventArgs e);
// This is a class which describes the event to the class that receives it.
// An EventArgs class must always derive from System.EventArgs.
public class MyEventArgs : EventArgs
{
private string EventInfo;
public MyEventArgs(string Text) {
EventInfo = Text;
}
public string GetInfo() {
return EventInfo;
}
}
// This next class is the one which contains an event and triggers it
// once an action is performed. For example, lets trigger this event
// once a variable is incremented over a particular value. Notice the
// event uses the MyEventHandler delegate to create a signature
// for the called function.
public class MyClass
{
public event MyEventHandler OnMaximum;
private int i;
private int Maximum = 10;
public int MyValue
{
get { return i; }
set
{
if(value <= Maximum) {
i = value;
}
else
{
// To make sure we only trigger the event if a handler is present
// we check the event to make sure it''s not null.
if(OnMaximum != null) {
OnMaximum(this, new MyEventArgs("You''ve entered " +
value.ToString() +
", but the maximum is " +
Maximum.ToString()));
}
}
}
}
}
class Program
{
// This is the actual method that will be assigned to the event handler
// within the above class. This is where we perform an action once the
// event has been triggered.
static void MaximumReached(object source, MyEventArgs e) {
Console.WriteLine(e.GetInfo());
}
static void Main(string[] args) {
// Now lets test the event contained in the above class.
MyClass MyObject = new MyClass();
MyObject.OnMaximum += new MyEventHandler(MaximumReached);
for(int x = 0; x <= 15; x++) {
MyObject.MyValue = x;
}
Console.ReadLine();
}
}
}
C # conoce dos términos, delegate
y event
. Vamos a empezar con el primero.
Delegar
Un delegate
es una referencia a un método. Al igual que puede crear una referencia a una instancia:
MyClass instance = myFactory.GetInstance();
Puede usar un delegado para crear una referencia a un método:
Action myMethod = myFactory.GetInstance;
Ahora que tiene esta referencia a un método, puede llamar al método a través de la referencia:
MyClass instance = myMethod();
Pero ¿por qué lo harías? También puedes simplemente llamar a myFactory.GetInstance()
directamente. En este caso puedes. Sin embargo, hay muchos casos en los que pensar en dónde no desea que el resto de la aplicación tenga conocimiento de myFactory
o llamar a myFactory.GetInstance()
directamente.
Una obvia es si desea poder reemplazar myFactory.GetInstance()
en myOfflineFakeFactory.GetInstance()
desde un lugar central (también conocido como patrón de método de fábrica ).
Patrón de método de fábrica
Entonces, si tiene una clase TheOtherClass
y necesita usar myFactory.GetInstance()
, así es como se verá el código sin los delegados (deberá informar a TheOtherClass
sobre el tipo de su myFactory
):
TheOtherClass toc;
//...
toc.SetFactory(myFactory);
class TheOtherClass
{
public void SetFactory(MyFactory factory)
{
// set here
}
}
Si usas delegados, no tienes que exponer el tipo de mi fábrica:
TheOtherClass toc;
//...
Action factoryMethod = myFactory.GetInstance;
toc.SetFactoryMethod(factoryMethod);
class TheOtherClass
{
public void SetFactoryMethod(Action factoryMethod)
{
// set here
}
}
Por lo tanto, puede otorgar 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 tales).
"Firma de mi método", ¿dónde escuché eso antes? O sí, interfaces !!! Las interfaces describen la firma de una clase entera. ¡Piense en los delegados que describen 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 llame al delegado, se ejecutarán múltiples métodos explícitamente adjuntos. Una referencia de objeto siempre puede hacer referencia solo a un objeto.
Las restricciones para un MulticastDelegate
son que la firma (método / delegado) no debe tener ningún valor de retorno ( void
) y las palabras clave out
y ref
no se usan en la firma. Obviamente, no puede llamar a dos métodos que devuelven un número y esperar que devuelvan el mismo número. Una vez que la firma cumple, el delegado es automáticamente un MulticastDelegate
.
Evento
Los eventos son solo propiedades (como los campos obtener, establecer, propiedades a instancia) que exponen la suscripción al delegado desde otros objetos. Estas propiedades, sin embargo, no admiten get; set ;. En su lugar son compatibles con agregar; eliminar;
Para que puedas tener:
Action myField;
public event Action MyProperty
{
add { myField += value; }
remove { myField -= value; }
}
Uso en la interfaz de usuario (WinForms)
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 ser referenciados por nuestro delegado, y somos un botón de UI, entonces: Puede preguntar a cualquier persona que esté interesada en si me hicieron clic para registrar su método con nosotros (a través del evento que expusimos). Podemos usar todos los métodos que se nos dieron y hacer referencia a ellos por nuestro delegado. Y luego, esperaremos y esperaremos ... hasta que un usuario aparezca y haga clic en ese botón, entonces tendremos suficientes razones para invocar al delegado. Y como el delegado hace referencia a todos los métodos que se nos han dado, todos esos métodos serán invocados. No sabemos qué hacen esos métodos, ni sabemos qué clase implementa esos métodos. Lo único que nos importa es que alguien haya estado interesado en que nos hicieran clic y nos dio una referencia a un método que cumplía con nuestra firma deseada.
Java
Los lenguajes como Java no tienen delegados. Ellos usan interfaces en su lugar. La forma en que lo hacen es pedirle a cualquier persona interesada en que "se nos haga clic" para que implemente cierta interfaz (con un cierto método al que podemos llamar), luego nos proporcione 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 al que podemos llamar'' cada vez que hacemos clic.
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:
public class Foo
{
public event EventHandler MyEvent;
}
Y luego puedes suscribirte al evento así:
Foo foo = new Foo();
foo.MyEvent += new EventHandler(this.OnMyEvent);
Con OnMyEvent () definido así:
private void OnMyEvent(object sender, EventArgs e)
{
MessageBox.Show("MyEvent fired!");
}
Cada vez que Foo
dispara MyEvent
, se OnMyEvent
su controlador OnMyEvent
.
No siempre tiene que usar una instancia de EventArgs
como segundo parámetro. Si desea incluir información adicional, puede usar una clase derivada de EventArgs
( EventArgs
es la base por convención). Por ejemplo, si observa algunos de los eventos definidos en Control
en WinForms o FrameworkElement
en WPF, puede ver ejemplos de eventos que pasan información adicional a los controladores de eventos.
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).
using System;
namespace test{
class MyTestApp{
//The Event Handler declaration
public delegate void EventAction();
//The Event Action Collection
//Equivalent to
// public List<EventAction> EventActions=new List<EventAction>();
//
public event EventAction EventActions;
//An Action
public void Hello(){
Console.WriteLine("Hello World of events!");
}
//Another Action
public void Goodbye(){
Console.WriteLine("Goodbye Cruel World of events!");
}
public static void Main(){
MyTestApp TestApp = new MyTestApp();
//Add actions to the collection
TestApp.EventActions += TestApp.Hello;
TestApp.EventActions += TestApp.Goodbye;
//Invoke all event actions
if (TestApp.EventActions!= null){
//this peculiar syntax hides the invoke
TestApp.EventActions();
//using the ''ActionCollection'' idea:
// foreach(EventAction action in TestApp.EventActions)
// action.Invoke();
}
}
}
}
Mi comprensión de los acontecimientos es;
Delegar:
Una variable para mantener la referencia al método / métodos a ser ejecutados. Esto hace posible pasar métodos como una variable.
Pasos para crear y convocar el evento:
El evento es una instancia de un delegado.
Como un evento es una instancia de un delegado, primero tenemos que definir al delegado.
Asigne el método / métodos que se ejecutarán cuando se active el evento ( Llamando al delegado )
Dispara el evento ( Llama al delegado )
Ejemplo:
using System;
namespace test{
class MyTestApp{
//The Event Handler declaration
public delegate void EventHandler();
//The Event declaration
public event EventHandler MyHandler;
//The method to call
public void Hello(){
Console.WriteLine("Hello World of events!");
}
public static void Main(){
MyTestApp TestApp = new MyTestApp();
//Assign the method to be called when the event is fired
TestApp.MyHandler = new EventHandler(TestApp.Hello);
//Firing the event
if (TestApp.MyHandler != null){
TestApp.MyHandler();
}
}
}
}
Para comprender a los manejadores de eventos, es necesario comprender a los delegates . 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. Eso es (1) el tipo de retorno y (2) los argumentos de entrada. Por ejemplo, si creamos un delegate void MyDelegate(object sender, EventArgs e)
, solo puede apuntar a los métodos que devuelven void
, y tomar un object
y EventArgs
. Algo así como un agujero cuadrado y una clavija cuadrada. Así que 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 que se ejecute algún código cuando algo suceda en otra parte del sistema, o "manejar el evento". Para hacer esto, creamos métodos específicos para el código que queremos ejecutar. 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 a los que llamar cuando se produce el evento. * Por supuesto, para poder llamar a un método, ¡necesitamos saber qué argumentos se le pasan! Usamos el delegado como el "contrato" entre el evento y todos los métodos específicos que se llamarán.
Así que el EventHandler
predeterminado (y muchos similares) representa una forma específica del método (de nuevo, void / object-EventArgs). Cuando declara un evento, está diciendo qué forma de método (EventHandler) invocará ese evento, especificando un delegado:
//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);
//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;
//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
//Do some stuff
}
//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened''s list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
//To raise the event within a method.
SomethingHappened("bar");
(* Esta es la clave de los eventos en .NET y elimina la "magia": un evento es realmente, debajo de las portadas, solo una lista de métodos de la misma "forma". La lista se almacena donde se encuentra el evento. Cuando el evento se "plantea", en realidad es simplemente "repase esta lista de métodos y llame a cada uno, usando 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).
Solo para agregar a las grandes respuestas existentes aquí, basándose en el código en el aceptado, que usa un delegate void MyEventHandler(string foo)
...
Debido a que el compilador conoce el tipo de delegado del evento SomethingHappened , esto:
myObj.SomethingHappened += HandleSomethingHappened;
Es totalmente equivalente a:
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
Y los manejadores también se pueden desregistrar con -=
así:
// -= removes the handler from the event''s list of "listeners":
myObj.SomethingHappened -= HandleSomethingHappened;
Para completar, el aumento del evento se puede hacer así, solo en la clase propietaria del evento:
//Firing the event is done by simply providing the arguments to the event:
var handler = SomethingHappened; // thread-local copy of the event
if (handler != null) // the event is null if there are no listeners!
{
handler("Hi there!");
}
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 verificar si era null
, y tendríamos una "diversión" NullReferenceException
allí.
C # 6 introdujo una buena mano corta para este patrón. Utiliza el operador de propagación nula.
SomethingHappened?.Invoke("Hi there!");
editor: donde suceden los eventos. El publicador debe especificar qué delegado está utilizando la clase y generar los argumentos necesarios, pasar esos argumentos y al delegado.
Suscriptor: donde suceda 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 ocurrirá con todos estos datos. Los suscriptores pueden crear métodos en su propia clase para responder a los eventos en la clase del editor, para que los suscriptores puedan responder a los eventos del editor.
//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyDelegate(string foo);
//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyDelegate MyEvent;
//Here is some code I want to be executed
//when SomethingHappened fires.
void MyEventHandler(string foo)
{
//Do some stuff
}
//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened''s list of "Event Handlers".
myObj.MyEvent += new MyDelegate (MyEventHandler);