Suche…


Einführung

Ein Ereignis ist eine Benachrichtigung, dass ein Ereignis aufgetreten ist (z. B. ein Mausklick) oder in einigen Fällen unmittelbar bevorsteht (z. B. Preisänderung).

Klassen können Ereignisse definieren und ihre Instanzen (Objekte) können diese Ereignisse auslösen. Eine Schaltfläche kann beispielsweise ein Click-Ereignis enthalten, das ausgelöst wird, wenn ein Benutzer darauf geklickt hat.

Event-Handler sind dann Methoden, die aufgerufen werden, wenn das entsprechende Ereignis ausgelöst wird. Ein Formular kann beispielsweise einen Clicked-Ereignishandler für jede Schaltfläche enthalten, die es enthält.

Parameter

Parameter Einzelheiten
EventArgsT Der von EventArgs abgeleitete Typ, der die Ereignisparameter enthält.
Veranstaltungsname Der Name der Veranstaltung
HandlerName Der Name des Event-Handlers.
SenderObject Das Objekt, das das Ereignis aufruft.
EventArguments Eine Instanz des EventArgsT-Typs, die die Ereignisparameter enthält.

Bemerkungen

Wenn Sie eine Veranstaltung auslösen:

  • Überprüfen Sie immer, ob der Delegat null . Ein null-Delegat bedeutet, dass das Ereignis keine Abonnenten hat. Das Auslösen eines Ereignisses ohne Abonnenten führt zu einer NullReferenceException .
6,0
  • Kopieren Sie den Delegaten (z. B. EventName ) in eine lokale Variable (z. B. eventName ), bevor Sie auf null überprüfen / das Ereignis eventName . Dadurch werden Race-Bedingungen in Umgebungen mit mehreren Threads vermieden:

Falsch :

    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.

Richtig :

    // 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
  • Verwenden Sie den nullbedingten Operator (?.), EventName?.Invoke(SenderObject, new EventArgsT()); anstatt den Delegaten in einer if EventName?.Invoke(SenderObject, new EventArgsT()); null zu überprüfen: EventName?.Invoke(SenderObject, new EventArgsT());
  • Bei der Verwendung von Aktion <> zum Deklarieren von Delegattypen muss die anonyme Methoden- / Ereignishandler-Signatur mit dem deklarierten anonymen Delegattyp in der Ereignisdeklaration übereinstimmen.

Ereignisse deklarieren und anheben

Ein Ereignis deklarieren

Sie können ein Ereignis für jede class oder struct mit der folgenden Syntax deklarieren:

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

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

Es gibt eine erweiterte Syntax für die Deklaration von Ereignissen, bei der Sie eine private Instanz des Ereignisses halten und eine öffentliche Instanz mithilfe von add und set Accessoren definieren. Die Syntax ist den C # -Eigenschaften sehr ähnlich. In allen Fällen sollte die oben dargestellte Syntax bevorzugt werden, da der Compiler Code ausgibt, um sicherzustellen, dass mehrere Threads Ereignishandler sicher zu dem Ereignis in Ihrer Klasse hinzufügen und entfernen können.

Das Ereignis auslösen

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

}

Beachten Sie, dass Ereignisse nur vom deklarierenden Typ ausgelöst werden können. Kunden können nur abonnieren / abbestellen.

Für C # -Versionen vor 6.0, bei denen EventName?.Invoke nicht unterstützt wird, EventName?.Invoke es sich, das Ereignis vor dem Aufruf einer temporären Variablen zuzuweisen ( EventName?.Invoke gewährleistet die Thread-Sicherheit in Fällen, in denen mehrere Threads das gleiche ausführen Code. Andernfalls kann in bestimmten Fällen, in denen mehrere Threads dieselbe Objektinstanz verwenden, eine NullReferenceException ausgelöst werden. In C # 6.0 gibt der Compiler Code aus, der dem im Codebeispiel für C # 6 gezeigten ähnlich ist.

Standard-Ereigniserklärung

Ereigniserklärung:

public event EventHandler<EventArgsT> EventName;

Event-Handler-Deklaration:

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

Anmeldung zum Event:

Dynamisch:

EventName += HandlerName;

Durch den Designer:

  1. Klicken Sie im Eigenschaftenfenster des Steuerelements auf die Schaltfläche Ereignisse (Blitz).
  2. Doppelklicken Sie auf den Ereignisnamen:

Geben Sie hier die Bildbeschreibung ein

  1. Visual Studio generiert den Ereigniscode:
private void Form1_Load(object sender, EventArgs e)
{

}

Aufrufen der Methode:

EventName(SenderObject, EventArguments);

Erklärung zum anonymen Ereignishandler

Ereigniserklärung:

public event EventHandler<EventArgsType> EventName;

Event-Handler-Deklaration mit Lambda-Operator => und Abonnieren des Events:

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

Event-Handler-Deklaration unter Verwendung der anonymen Methodenmethode für Delegaten :

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

Deklaration und Subskription eines Ereignishandlers, der den Parameter des Ereignisses nicht verwendet und daher die obige Syntax verwenden kann, ohne Parameter angeben zu müssen:

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

Aufrufen der Veranstaltung:

EventName?.Invoke(SenderObject, EventArguments);

Nicht-Standard-Ereigniserklärung

Ereignisse können einen beliebigen EventHandler , nicht nur EventHandler und EventHandler<T> . Zum Beispiel:

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

Dies wird ähnlich wie bei Standard- EventHandler Ereignissen verwendet:

//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 ist möglich, mehrere Ereignisse desselben Typs in einer einzigen Anweisung zu deklarieren, ähnlich wie bei Feldern und lokalen Variablen (dies ist jedoch oft eine schlechte Idee):

public event EventHandler Event1, Event2, Event3;

Hiermit werden drei separate Ereignisse ( Event1 , Event2 und Event3 ) vom Typ EventHandler .
Anmerkung: Obwohl einige Compiler diese Syntax in Schnittstellen und Klassen akzeptieren, enthält die C # -Spezifikation (v5.0 §13.2.3) Grammatik für Schnittstellen, die dies nicht zulassen. Daher ist die Verwendung dieser Schnittstelle in Schnittstellen bei verschiedenen Compilern unzuverlässig.

Erstellen von benutzerdefinierten EventArgs mit zusätzlichen Daten

Für benutzerdefinierte Ereignisse sind normalerweise benutzerdefinierte Ereignisargumente erforderlich, die Informationen zum Ereignis enthalten. Beispielsweise enthält MouseEventArgs das von Mausereignissen wie MouseDown oder MouseUp Ereignissen verwendet wird, Informationen zum Location oder zu Buttons denen das Ereignis generiert wurde.

Wenn Sie neue Ereignisse erstellen, erstellen Sie ein benutzerdefiniertes Ereignis arg:

  • Erstellen Sie eine von EventArgs Klasse, und definieren Sie Eigenschaften für die erforderlichen Daten.
  • Als Konvention sollte der Name der Klasse mit EventArgs .

Beispiel

Im folgenden Beispiel erstellen wir ein PriceChangingEventArgs Ereignis für die Price Eigenschaft einer Klasse. Die Ereignisdatenklasse enthält einen CurrentPrice und einen NewPrice . Das Ereignis wird ausgelöst, wenn Sie der Eigenschaft Price einen neuen Wert zuweisen und den Verbraucher wissen, dass sich der Wert ändert, und ihn über den aktuellen Preis und den neuen Preis informieren.

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

Produkt

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

Sie können das Beispiel verbessern, indem Sie dem Verbraucher erlauben, den neuen Wert zu ändern. Der Wert wird dann für die Eigenschaft verwendet. Dazu genügt es, diese Änderungen in Klassen anzuwenden.

Ändern Sie die Definition von NewPrice , dass sie einstellbar ist:

public int NewPrice { get; set; }

Ändern Sie die Definition von Price , um e.NewPrice als Eigenschaftswert zu verwenden, nachdem Sie OnPriceChanging :

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

Stornierbares Ereignis erstellen

Ein stornierbares Ereignis kann von einer Klasse FormClosing werden, wenn eine Aktion ausgeführt werden soll, die abgebrochen werden kann, beispielsweise das FormClosing Ereignis eines Form .

So erstellen Sie ein solches Ereignis:

  • Erstellen Sie ein neues Ereignisargument, das von CancelEventArgs und fügen Sie zusätzliche Eigenschaften für Ereignisdaten hinzu.
  • Erstellen Sie ein Ereignis mit EventHandler<T> und verwenden Sie die neue EventHandler<T> die Sie erstellt haben.

Beispiel

Im folgenden Beispiel erstellen wir ein PriceChangingEventArgs Ereignis für die Price Eigenschaft einer Klasse. Die Ereignisdatenklasse enthält einen Value der den Verbraucher über das Neue informiert. Das Ereignis wird ausgelöst, wenn Sie der Eigenschaft Price einen neuen Wert zuweisen. Der Verbraucher wird darüber informiert, dass sich der Wert ändert, und lässt das Ereignis abbrechen. Wenn der Verbraucher das Ereignis abbricht, wird der vorherige Wert für Price verwendet:

PriceChangingEventArgs

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

Produkt

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

Ereignis-Eigenschaften

Wenn eine Klasse eine große Anzahl von Ereignissen auslöst, sind die Speicherkosten für ein Feld pro Delegat möglicherweise nicht akzeptabel. .NET Framework stellt Ereigniseigenschaften für diese Fälle bereit. Auf diese Weise können Sie eine andere Datenstruktur wie EventHandlerList zum Speichern von Ereignisdelegaten verwenden:

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

Dieser Ansatz ist in GUI-Frameworks wie WinForms weit verbreitet, wo Steuerelemente Dutzende und sogar Hunderte von Ereignissen enthalten können.

Beachten Sie, dass EventHandlerList nicht threadsicher ist. Wenn Sie also erwarten, dass Ihre Klasse von mehreren Threads verwendet wird, müssen Sie EventHandlerList oder einen anderen Synchronisationsmechanismus hinzufügen (oder einen Speicher verwenden, der Thread-Sicherheit bietet).



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow