Ricerca…


introduzione

Un evento è una notifica che qualcosa si è verificato (come un clic del mouse) o, in alcuni casi, sta per verificarsi (come una variazione di prezzo).

Le classi possono definire eventi e le loro istanze (oggetti) possono generare questi eventi. Ad esempio, un pulsante può contenere un evento Click che viene generato quando un utente ha fatto clic su di esso.

I gestori di eventi sono quindi i metodi che vengono richiamati quando viene generato l'evento corrispondente. Un modulo può contenere un gestore di eventi Clicked per ogni pulsante che contiene, per esempio.

Parametri

Parametro Dettagli
EventArgsT Il tipo che deriva da EventArgs e contiene i parametri dell'evento.
Nome dell'evento Il nome dell'evento.
handlerName Il nome del gestore di eventi.
SenderObject L'oggetto che sta invocando l'evento.
EventArguments Un'istanza del tipo EventArgsT che contiene i parametri dell'evento.

Osservazioni

Quando si alza un evento:

  • Controllare sempre se il delegato è null . Un delegato nullo significa che l'evento non ha sottoscrittori. Aumentare un evento senza abbonati comporterà una NullReferenceException .
6.0
  • Copia il delegato (ad es. EventName ) in una variabile locale (ad es. eventName ) prima di verificare la presenza di null / raise dell'evento. Questo evita condizioni di gara in ambienti multi-thread:

Sbagliato :

    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 destra :

    // 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
  • Utilizzare l'operatore null-condizionale (?.) Per aumentare il metodo anziché il controllo null del delegato per i sottoscrittori in un'istruzione if : EventName?.Invoke(SenderObject, new EventArgsT());
  • Quando si utilizza l'azione <> per dichiarare i tipi di delegato, la firma del gestore di un metodo / evento anonimo deve essere uguale al tipo di delegato anonimo dichiarato nella dichiarazione di evento.

Dichiarare e sollevare eventi

Dichiarazione di un evento

Puoi dichiarare un evento su qualsiasi class o struct usando la seguente sintassi:

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

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

Esiste una sintassi espansa per la dichiarazione degli eventi, in cui si detiene un'istanza privata dell'evento e si definisce un'istanza pubblica utilizzando add e set accessors. La sintassi è molto simile alle proprietà C #. In tutti i casi, la sintassi sopra illustrata dovrebbe essere preferita, poiché il compilatore emette il codice per garantire che più thread possano aggiungere e rimuovere in modo sicuro i gestori di eventi all'evento della classe.

Alzare l'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);

}

Tieni presente che gli eventi possono essere generati solo dal tipo di dichiarazione. I clienti possono solo iscriversi / annullare l'iscrizione.

Per le versioni C # precedenti alla 6.0, dove EventName?.Invoke non è supportato, è buona pratica assegnare l'evento a una variabile temporanea prima del richiamo, come mostrato nell'esempio, che garantisce la sicurezza del thread nei casi in cui più thread eseguono lo stesso codice. In caso NullReferenceException possibile che venga generata una NullReferenceException in alcuni casi in cui più thread utilizzano la stessa istanza di oggetto. In C # 6.0, il compilatore emette un codice simile a quello mostrato nell'esempio di codice per C # 6.

Dichiarazione di eventi standard

Dichiarazione di evento:

public event EventHandler<EventArgsT> EventName;

Dichiarazione del gestore eventi:

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

Iscrizione all'evento:

dinamicamente:

EventName += HandlerName;

Attraverso il Designer:

  1. Fare clic sul pulsante Eventi nella finestra delle proprietà del controllo (Lightning bolt)
  2. Fai doppio clic sul nome dell'evento:

inserisci la descrizione dell'immagine qui

  1. Visual Studio genererà il codice evento:
private void Form1_Load(object sender, EventArgs e)
{

}

Invocare il metodo:

EventName(SenderObject, EventArguments);

Dichiarazione del gestore eventi anonimo

Dichiarazione di evento:

public event EventHandler<EventArgsType> EventName;

Dichiarazione del gestore eventi che utilizza l' operatore lambda => e si iscrive all'evento:

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

Dichiarazione del gestore eventi che utilizza la sintassi del metodo anonimo delegato :

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

Dichiarazione e sottoscrizione di un gestore di eventi che non utilizza il parametro dell'evento e pertanto può utilizzare la sintassi precedente senza dover specificare i parametri:

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

Invocazione dell'evento:

EventName?.Invoke(SenderObject, EventArguments);

Dichiarazione di eventi non standard

Gli eventi possono essere di qualsiasi tipo di delegato, non solo EventHandler e EventHandler<T> . Per esempio:

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

Questo è usato in modo simile agli eventi EventHandler standard:

//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, ...);

È possibile dichiarare più eventi dello stesso tipo in una singola istruzione, in modo simile ai campi e alle variabili locali (sebbene ciò possa spesso essere una cattiva idea):

public event EventHandler Event1, Event2, Event3;

Questo dichiara tre eventi separati ( Event1 , Event2 ed Event3 ) tutti di tipo EventHandler .
Nota: sebbene alcuni compilatori possano accettare questa sintassi sia nelle interfacce che nelle classi, la specifica C # (v5.0 §13.2.3) fornisce la grammatica per le interfacce che non lo consentono, quindi l'utilizzo di questo nelle interfacce potrebbe non essere affidabile con diversi compilatori.

Creazione di eventi personalizzati contenenti dati aggiuntivi

Gli eventi personalizzati di solito richiedono argomenti di eventi personalizzati contenenti informazioni sull'evento. Ad esempio MouseEventArgs utilizzato dagli eventi del mouse come MouseDown o MouseUp , contiene informazioni sulla Location o sui Buttons utilizzati per generare l'evento.

Quando si creano nuovi eventi, per creare un evento personalizzato arg:

  • Creare una classe derivante da EventArgs e definire le proprietà per i dati necessari.
  • Come convenzione, il nome della classe dovrebbe terminare con EventArgs .

Esempio

Nell'esempio seguente, creiamo un evento PriceChangingEventArgs per la proprietà Price di una classe. La classe di dati evento contiene un CurrentPrice e NewPrice . L'evento aumenta quando si assegna un nuovo valore alla proprietà Price e si fa sapere al consumatore che il valore sta cambiando e consente loro di conoscere il prezzo corrente e il nuovo prezzo:

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; }
}

Prodotto

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);
    }
}

È possibile migliorare l'esempio consentendo al consumatore di modificare il nuovo valore e quindi il valore verrà utilizzato per la proprietà. Per fare ciò è sufficiente applicare questi cambiamenti nelle classi.

Modifica la definizione di NewPrice per essere impostabile:

public int NewPrice { get; set; }

Modifica la definizione di Price per utilizzare e.NewPrice come valore della proprietà, dopo aver chiamato OnPriceChanging :

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

Creare un evento cancellabile

Un evento cancellabile può essere generato da una classe quando sta per eseguire un'azione che può essere annullata, come ad esempio l'evento FormClosing di un Form .

Per creare un evento del genere:

  • Creare un nuovo argomento evento derivato da CancelEventArgs e aggiungere proprietà aggiuntive per i dati degli eventi.
  • Crea un evento utilizzando EventHandler<T> e utilizza la nuova classe di evento event cancel che hai creato.

Esempio

Nell'esempio seguente, creiamo un evento PriceChangingEventArgs per la proprietà Price di una classe. La classe di dati dell'evento contiene un Value che consente al consumatore di conoscere il nuovo. L'evento aumenta quando si assegna un nuovo valore alla proprietà Price e si consente al consumatore di sapere che il valore sta cambiando e di consentire loro di annullare l'evento. Se il consumatore annulla l'evento, verrà utilizzato il valore precedente per Price :

PriceChangingEventArgs

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

Prodotto

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);
    }
}

Proprietà dell'evento

Se una classe aumenta di molto il numero di eventi, il costo di archiviazione di un campo per delegato potrebbe non essere accettabile. .NET Framework fornisce le proprietà degli eventi per questi casi. In questo modo è possibile utilizzare un'altra struttura dati come EventHandlerList per memorizzare i delegati dell'evento:

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);
    }
}

Questo approccio è ampiamente utilizzato in framework GUI come WinForms in cui i controlli possono avere dozzine e persino centinaia di eventi.

Notare che EventHandlerList non è thread-safe, quindi se si prevede che la classe venga utilizzata da più thread, sarà necessario aggiungere istruzioni di blocco o altri meccanismi di sincronizzazione (o utilizzare una memoria che fornisce sicurezza di thread).



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow