Buscar..


Introducción

Un evento es una notificación de que algo ocurrió (como un clic del mouse) o, en algunos casos, está a punto de ocurrir (como un cambio de precio).

Las clases pueden definir eventos y sus instancias (objetos) pueden provocar estos eventos. Por ejemplo, un botón puede contener un evento de clic que se genera cuando un usuario lo ha hecho clic.

Los controladores de eventos son métodos que se llaman cuando se genera el evento correspondiente. Un formulario puede contener un controlador de eventos de clic para cada botón que contiene, por ejemplo.

Parámetros

Parámetro Detalles
EventArgsT El tipo que se deriva de EventArgs y contiene los parámetros del evento.
Nombre del evento El nombre del evento.
Nombre del controlador El nombre del controlador de eventos.
SenderObject El objeto que invoca el evento.
EventosArgumentos Una instancia del tipo EventArgsT que contiene los parámetros del evento.

Observaciones

Al plantear un evento:

  • Compruebe siempre si el delegado es null . Un delegado nulo significa que el evento no tiene suscriptores. Si se genera un evento sin suscriptores, se obtendrá una NullReferenceException .
6.0
  • Copie el delegado (por ejemplo, EventName ) en una variable local (por ejemplo, eventName ) antes de verificar si el evento es nulo / eventName . Esto evita las condiciones de carrera en entornos de subprocesos múltiples:

Mal

    if(Changed != null)      // Changed has 1 subscriber at this point
                             // In another thread, that one subscriber decided to unsubscribe
        Changed(this, args); // `Changed` is now null, `NullReferenceException` is thrown.

A la derecha

    // Cache the "Changed" event as a local. If it is not null, then use
    // the LOCAL variable (handler) to raise the event, NOT the event itself.
    var handler = Changed;
    if(handler != null)
        handler(this, args);
6.0
  • Use el operador condicional nulo (?.) Para elevar el método en lugar de verificar nulo al delegado en busca de suscriptores en una instrucción if : EventName?.Invoke(SenderObject, new EventArgsT());
  • Cuando se usa Acción <> para declarar tipos de delegados, la firma del controlador de eventos / eventos anónimo debe ser la misma que el tipo de delegado anónimo declarado en la declaración de eventos.

Declarar y levantar eventos

Declarar un evento

Puedes declarar un evento en cualquier class o struct usando la siguiente sintaxis:

public class MyClass
{
    // Declares the event for MyClass
    public event EventHandler MyEvent;

    // Raises the MyEvent event
    public void RaiseEvent()
    {
        OnMyEvent();
    }
}    

Hay una sintaxis expandida para declarar eventos, donde se tiene una instancia privada del evento, y se define una instancia pública utilizando los accesores de add y set . La sintaxis es muy similar a las propiedades de C #. En todos los casos, se debe preferir la sintaxis demostrada anteriormente, ya que el compilador emite código para ayudar a garantizar que varios subprocesos puedan agregar y eliminar de forma segura los controladores de eventos al evento en su clase.

Elevando el evento

6.0
private void OnMyEvent()
{
    EventName?.Invoke(this, EventArgs.Empty); 
}
6.0
private void OnMyEvent()
{
    // Use a local for EventName, because another thread can modify the
    // public EventName between when we check it for null, and when we
    // raise the event.
    var eventName = EventName;

    // If eventName == null, then it means there are no event-subscribers,
    // and therefore, we cannot raise the event.
    if(eventName != null)
        eventName(this, EventArgs.Empty);

}

Tenga en cuenta que los eventos solo pueden ser provocados por el tipo declarante. Los clientes solo pueden suscribirse / darse de baja.

Para versiones de C # anteriores a 6.0, donde EventName?.Invoke no es compatible, es una buena práctica asignar el evento a una variable temporal antes de la invocación, como se muestra en el ejemplo, que garantiza la seguridad de subprocesos en los casos en que varios subprocesos ejecutan el mismo código. Si no lo hace, puede provocar una NullReferenceException en ciertos casos en los que varios subprocesos utilizan la misma instancia de objeto. En C # 6.0, el compilador emite un código similar al que se muestra en el ejemplo de código para C # 6.

Declaración de evento estándar

Declaración de evento:

public event EventHandler<EventArgsT> EventName;

Declaración del manejador de eventos:

public void HandlerName(object sender, EventArgsT args) { /* Handler logic */ }

Suscribiéndose al evento:

Dinamicamente:

EventName += HandlerName;

A través del Diseñador:

  1. Haga clic en el botón Eventos en la ventana de propiedades del control (Lightening bolt)
  2. Haga doble clic en el nombre del evento:

introduzca la descripción de la imagen aquí

  1. Visual Studio generará el código del evento:
private void Form1_Load(object sender, EventArgs e)
{

}

Invocando el método:

EventName(SenderObject, EventArguments);

Declaración de manejador de eventos anónimos

Declaración de evento:

public event EventHandler<EventArgsType> EventName;

Declaración del manejador de eventos utilizando el operador lambda => y suscribiéndose al evento:

EventName += (obj, eventArgs) => { /* Handler logic */ };

Declaración del controlador de eventos mediante la sintaxis de métodos anónimos delegados :

EventName += delegate(object obj, EventArgsType eventArgs) { /* Handler Logic */ };

Declaración y suscripción de un controlador de eventos que no usa el parámetro del evento, por lo que puede usar la sintaxis anterior sin necesidad de especificar parámetros:

EventName += delegate { /* Handler Logic */ }

Invocando el evento:

EventName?.Invoke(SenderObject, EventArguments);

Declaración de evento no estándar

Los eventos pueden ser de cualquier tipo de delegado, no solo EventHandler y EventHandler<T> . Por ejemplo:

//Declaring an event
public event Action<Param1Type, Param2Type, ...> EventName;

Esto se usa de manera similar a los eventos de EventHandler estándar:

//Adding a named event handler
public void HandlerName(Param1Type parameter1, Param2Type parameter2, ...) {
    /* Handler logic */
}
EventName += HandlerName;

//Adding an anonymous event handler
EventName += (parameter1, parameter2, ...) => { /* Handler Logic */ };

//Invoking the event
EventName(parameter1, parameter2, ...);

Es posible declarar varios eventos del mismo tipo en una sola declaración, similar a los campos y las variables locales (aunque esto puede ser una mala idea):

public event EventHandler Event1, Event2, Event3;

Esto declara tres eventos separados ( Event1 , Event2 y Event3 ) todos de tipo EventHandler .
Nota: aunque algunos compiladores pueden aceptar esta sintaxis en las interfaces y en las clases, la especificación C # (v5.0 §13.2.3) proporciona una gramática para las interfaces que no lo permiten, por lo que su uso en las interfaces puede ser poco confiable con diferentes compiladores.

Creación de EventArgs personalizados que contienen datos adicionales

Los eventos personalizados generalmente necesitan argumentos de eventos personalizados que contengan información sobre el evento. Por ejemplo, MouseEventArgs utilizado por eventos del mouse como MouseDown o MouseUp , contiene información sobre la Location o los Buttons que se utilizan para generar el evento.

Al crear nuevos eventos, para crear un evento personalizado arg:

  • Cree una clase derivada de EventArgs y defina las propiedades para los datos necesarios.
  • Como convención, el nombre de la clase debe terminar con EventArgs .

Ejemplo

En el siguiente ejemplo, creamos un evento PriceChangingEventArgs para la propiedad Price de una clase. La clase de datos de evento contiene un CurrentPrice y un NewPrice . El evento aumenta cuando asigna un nuevo valor a la propiedad Price y le informa al consumidor que el valor está cambiando y le informa sobre el precio actual y el nuevo precio:

PriceChangingEventArgs

public class PriceChangingEventArgs : EventArgs
{
    public PriceChangingEventArgs(int currentPrice, int newPrice)
    {
        this.CurrentPrice = currentPrice;
        this.NewPrice = newPrice;
    }

    public int CurrentPrice { get; private set; }
    public int NewPrice { get; private set; }
}

Producto

public class Product
{
    public event EventHandler<PriceChangingEventArgs> PriceChanging;

    int price;
    public int Price
    {
        get { return price; }
        set
        {
            var e = new PriceChangingEventArgs(price, value);
            OnPriceChanging(e);
            price = value;
        }
    }

    protected void OnPriceChanging(PriceChangingEventArgs e)
    {
        var handler = PriceChanging;
        if (handler != null)
            handler(this, e);
    }
}

Puede mejorar el ejemplo permitiendo que el consumidor cambie el nuevo valor y luego el valor se usará para la propiedad. Para hacerlo es suficiente aplicar estos cambios en las clases.

Cambie la definición de NewPrice para ser configurable:

public int NewPrice { get; set; }

Cambie la definición de Price para usar e.NewPrice como valor de propiedad, después de llamar a OnPriceChanging :

int price;
public int Price
{
    get { return price; }
    set
    {
        var e = new PriceChangingEventArgs(price, value);
        OnPriceChanging(e);
        price = e.NewPrice;
    }
}

Creando evento cancelable

Un evento cancelable puede ser generado por una clase cuando está a punto de realizar una acción que puede cancelarse, como el evento FormClosing de un Form .

Para crear tal evento:

  • Cree un nuevo evento CancelEventArgs en CancelEventArgs y agregue propiedades adicionales para los datos del evento.
  • Cree un evento usando EventHandler<T> y use la nueva clase arg de evento de cancelación que creó.

Ejemplo

En el siguiente ejemplo, creamos un evento PriceChangingEventArgs para la propiedad Price de una clase. La clase de datos del evento contiene un Value que le permite al consumidor conocer la nueva. El evento aumenta cuando asigna un nuevo valor a la propiedad Price y le informa al consumidor que el valor está cambiando y le permite cancelar el evento. Si el consumidor cancela el evento, se utilizará el valor anterior para Price :

PriceChangingEventArgs

public class PriceChangingEventArgs : CancelEventArgs
{
    int value;
    public int Value
    {
        get { return value; }
    }
    public PriceChangingEventArgs(int value)
    {
        this.value = value;
    }
}

Producto

public class Product
{
    int price;
    public int Price
    {
        get { return price; }
        set
        {
            var e = new PriceChangingEventArgs(value);
            OnPriceChanging(e);
            if (!e.Cancel)
                price = value;
        }
    }

    public event EventHandler<PriceChangingEventArgs> PropertyChanging;
    protected void OnPriceChanging(PriceChangingEventArgs e)
    {
        var handler = PropertyChanging;
        if (handler != null)
            PropertyChanging(this, e);
    }
}

Propiedades del evento

Si una clase aumenta la cantidad de eventos, el costo de almacenamiento de un campo por delegado puede no ser aceptable. .NET Framework proporciona propiedades de eventos para estos casos. De esta manera, puede utilizar otra estructura de datos como EventHandlerList para almacenar delegados de eventos:

public class SampleClass 
{
    // Define the delegate collection.
    protected EventHandlerList eventDelegates = new EventHandlerList();

    // Define a unique key for each event.
    static readonly object someEventKey = new object();

    // Define the SomeEvent event property.
    public event EventHandler SomeEvent
    {
        add
        {
            // Add the input delegate to the collection.
            eventDelegates.AddHandler(someEventKey, value);
        }
        remove
        {
            // Remove the input delegate from the collection.
            eventDelegates.RemoveHandler(someEventKey, value);
        }
    }

    // Raise the event with the delegate specified by someEventKey
    protected void OnSomeEvent(EventArgs e)
    {
        var handler = (EventHandler)eventDelegates[someEventKey];
        if (handler != null)
            handler(this, e);
    }
}

Este enfoque se usa ampliamente en marcos de GUI como WinForms, donde los controles pueden tener docenas e incluso cientos de eventos.

Tenga en cuenta que EventHandlerList no es seguro para subprocesos, por lo que si espera que su clase se use desde varios subprocesos, deberá agregar declaraciones de bloqueo u otro mecanismo de sincronización (o usar un almacenamiento que proporcione seguridad de subprocesos).



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow