Verarbeiten und Auslösen von Ereignissen

Ereignisse in .NET basieren auf dem Delegatmodell. Das Delegatmodell folgt dem Beobachterentwurfsmuster, mit dem sich ein Abonnent bei einem Anbieter registrieren und Benachrichtigungen von diesem empfangen kann. Von einem Ereignissender wird eine Benachrichtigung erstellt, die angibt, dass ein Ereignis aufgetreten ist. Diese Benachrichtigung wird dann vom Ereignisempfänger empfangen, und eine Antwort wird definiert. In diesem Artikel werden die Hauptkomponenten des Delegatmodells, das Verwenden von Ereignissen in Anwendungen und das Implementieren von Ereignissen im Code beschrieben.

Ereignisse

Ein Ereignis ist eine Meldung, die von einem Objekt gesendet wird, um das Auftreten einer Aktion zu signalisieren. Die Aktion kann durch Benutzerinteraktionen wie das Klicken auf eine Schaltfläche verursacht werden, oder sie kann durch eine andere Programmlogik, z. B. das Ändern eines Eigenschaftswerts, ausgelöst werden. Das Objekt, von dem das Ereignis ausgelöst wird, wird als Ereignissender bezeichnet. Dem Ereignissender ist nicht bekannt, welches Objekt oder welche Methode die ausgelösten Ereignisse empfangen (behandeln) wird. Das Ereignis ist in der Regel ein Member des Ereignissenders. Beispielsweise ist das Click-Ereignis ein Member der Klasse Button, und das PropertyChanged-Ereignis ist ein Member der Klasse, von der die INotifyPropertyChanged-Schnittstelle implementiert wird.

Zum Definieren eines Ereignisses verwenden Sie entweder das Schlüsselwort event (in C#) oder das Schlüsselwort Event (in Visual Basic) in der Signatur der Ereignisklasse und geben den Typ des Delegaten für das Ereignis an. Delegaten werden im nächsten Abschnitt erläutert.

In der Regel fügen Sie zum Auslösen eines Ereignisses eine Methode hinzu, die als protected und virtual (in C#) bzw. Protected und Overridable (in Visual Basic) gekennzeichnet ist. Nennen Sie diese Methode OnEventName (zum Beispiel OnDataReceived). Von dieser Methode muss ein Parameter akzeptiert werden, der ein Ereignisdatenobjekt angibt, welches ein Objekt des Typs EventArgs oder eines abgeleiteten Typs ist. Sie stellen diese Methode bereit, damit abgeleitete Klassen die Logik zum Auslösen des Ereignisses überschreiben können. Eine abgeleitete Klasse sollte immer die OnEventName-Methode der Basisklasse aufrufen, um sicherzustellen, dass registrierte Delegaten das Ereignis empfangen.

Im folgenden Beispiel wird die Deklaration eines Ereignisses namens ThresholdReachederläutert. Das Ereignis wird dem EventHandler-Delegaten zugeordnet, und es wird in einer Methode namens OnThresholdReached ausgelöst.

class Counter
{
    public event EventHandler ThresholdReached;

    protected virtual void OnThresholdReached(EventArgs e)
    {
        EventHandler handler = ThresholdReached;
        handler?.Invoke(this, e);
    }

    // provide remaining implementation for the class
}
Public Class Counter
    Public Event ThresholdReached As EventHandler

    Protected Overridable Sub OnThresholdReached(e As EventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub

    ' provide remaining implementation for the class
End Class

Delegaten

Ein Delegat ist ein Typ, der einen Verweis auf eine Methode enthält. Ein Delegat wird mit einer Signatur deklariert, die den Rückgabetyp und die Parameter für die Methoden angibt, auf die er verweist. Er kann nur Verweise auf Methoden enthalten, die mit der Signatur übereinstimmen. Ein Delegat entspricht insofern einem typsicheren Funktionszeiger oder einem Rückruf. Mit einer Delegatdeklaration ist eine Delegatklasse ausreichend definiert.

Delegaten können in .NET vielfältig verwendet werden. Im Kontext der Ereignisse ist ein Delegat ein Mittler (oder ein zeigerähnlicher Mechanismus) zwischen der Ereignisquelle und dem Code, mit dem das Ereignis behandelt wird. Sie weisen einen Delegaten einem Ereignis zu, indem Sie den Delegattyp in der Ereignisdeklaration, wie im Beispiel des vorherigen Abschnitts veranschaulicht, einschließen. Weitere Informationen zu Delegaten finden Sie unter der Delegate-Klasse.

.NET stellt die Delegaten EventHandler und EventHandler<TEventArgs> zur Unterstützung der meisten Ereignisszenarien bereit. Verwenden Sie den EventHandler-Delegaten für alle Ereignisse, in denen keine Ereignisdaten enthalten sind. Verwenden Sie den EventHandler<TEventArgs>-Delegaten für Ereignisse, die Daten über das Ereignis enthalten. Diese Delegaten verfügen über keinen Rückgabetypwert und akzeptieren zwei Parameter (ein Objekt für die Ereignisquelle sowie ein Objekt für Ereignisdaten).

Delegaten sind Multicastdelegaten, d.h., sie können Verweise auf mehrere Methoden für die Ereignisbehandlung enthalten. Weitere Informationen finden Sie auf der Delegate-Referenzseite. Delegaten bieten Flexibilität und eine genaue Steuerung bei der Ereignisbehandlung. Ein Delegat fungiert als ein Ereignisverteiler für die Klasse, die das Ereignis auslöst, indem er eine Liste der registrierten Ereignishandler für das Ereignis verwaltet.

Für Szenarien, in denen die Delegaten EventHandler und EventHandler<TEventArgs> nicht funktionieren, können Sie einen Delegaten definieren. Szenarien, für die Sie einen Delegaten definieren müssen, sind sehr selten, z. B. bei der Arbeit mit Code, von dem keine Generics erkannt werden. Markieren Sie einen Delegaten mit dem Schlüsselwort delegate (in C#) und dem Schlüsselwort Delegate (in Visual Basic) in der Deklaration. Im folgenden Beispiel wird die Deklaration eines Delegaten namens ThresholdReachedEventHandler erläutert.

public delegate void ThresholdReachedEventHandler(object sender, ThresholdReachedEventArgs e);
Public Delegate Sub ThresholdReachedEventHandler(sender As Object, e As ThresholdReachedEventArgs)

Ereignisdaten

Daten, die einem Ereignis zugewiesen sind, können durch eine Ereignisdatenklasse bereitgestellt werden. In .NET werden viele Ereignisdatenklassen für die Verwendung in Ihren Anwendungen bereitgestellt. Zum Beispiel ist die SerialDataReceivedEventArgs-Klasse die Ereignisdatenklasse für das SerialPort.DataReceived-Ereignis. .NET folgt einem Benennungsmuster, bei dem alle Ereignisdatenklassen auf EventArgs enden. Sie bestimmen, welche Ereignisdatenklasse einem Ereignis zugeordnet wird, indem Sie den Delegaten nach dem Ereignis durchsuchen. Zum Beispiel enthält der SerialDataReceivedEventHandler-Delegat die SerialDataReceivedEventArgs-Klasse als einen seiner Parameter.

Die EventArgs-Klasse ist der Basistyp aller Ereignisdatenklassen. EventArgs ist auch die Klasse, die bei einem Ereignis verwendet wird, dem keine Daten zugeordnet sind. Wenn Sie ein Ereignis erstellen, nur um andere Klassen über ein Geschehnis zu benachrichtigen, ohne dass Daten übergeben werden, schließen Sie die EventArgs-Klasse als zweiten Parameter im Delegaten ein. Sie können den EventArgs.Empty-Wert übergeben, wenn keine Daten bereitgestellt werden. Im EventHandler-Delegat ist die EventArgs-Klasse als Parameter enthalten.

Wenn Sie eine benutzerdefinierte Ereignisdatenklasse erstellen möchten, erstellen Sie eine von EventArgs abgeleitete Klasse, und stellen Sie dann alle Member bereit, die zum Übergeben der mit dem Ereignis verknüpften Daten erforderlich sind. In der Regel sollten Sie dem Benennungsmuster von .NET folgen und den Ereignisdaten-Klassennamen auf EventArgs enden lassen.

Im folgenden Beispiel wird eine Ereignisdatenklasse ThresholdReachedEventArgs veranschaulicht. Darin sind Eigenschaften enthalten, die für das ausgelöste Ereignis spezifisch sind.

public class ThresholdReachedEventArgs : EventArgs
{
    public int Threshold { get; set; }
    public DateTime TimeReached { get; set; }
}
Public Class ThresholdReachedEventArgs
    Inherits EventArgs

    Public Property Threshold As Integer
    Public Property TimeReached As DateTime
End Class

Ereignishandler

Um auf ein Ereignis zu reagieren, definieren Sie eine Ereignishandlermethode im Ereignisempfänger. Diese Methode muss der Signatur des Delegaten für das von Ihnen behandelte Ereignis entsprechen. Im Ereignishandler führen Sie die Aktionen aus, die beim Auslösen des Ereignisses erforderlich sind, zum Beispiel das Sammeln von Benutzereingaben, nachdem der Benutzer auf eine Schaltfläche geklickt hat. Um bei Auftreten des Ereignisses Benachrichtigungen zu empfangen, muss die Ereignishandlermethode das Ereignis abonnieren.

Im folgenden Beispiel wird die Ereignishandlermethode c_ThresholdReached dargestellt, die der Signatur für den EventHandler-Delegaten entspricht. Die Methode abonniert das ThresholdReached-Ereignis.

class Program
{
    static void Main()
    {
        var c = new Counter();
        c.ThresholdReached += c_ThresholdReached;

        // provide remaining implementation for the class
    }

    static void c_ThresholdReached(object sender, EventArgs e)
    {
        Console.WriteLine("The threshold was reached.");
    }
}
Module Module1

    Sub Main()
        Dim c As New Counter()
        AddHandler c.ThresholdReached, AddressOf c_ThresholdReached

        ' provide remaining implementation for the class
    End Sub

    Sub c_ThresholdReached(sender As Object, e As EventArgs)
        Console.WriteLine("The threshold was reached.")
    End Sub
End Module

Statische und dynamische Ereignishandler

Mit .NET können sich Abonnenten entweder statisch oder dynamisch für Ereignisbenachrichtigungen registrieren. Statische Ereignishandler sind für die gesamte Lebensdauer der Klasse gültig, deren Ereignisse sie behandeln. Dynamische Ereignishandler werden bei der Programmausführung explizit aktiviert und deaktiviert. Dies erfolgt in der Regel als Reaktion auf eine bedingte Programmlogik. So können sie beispielsweise verwendet werden, wenn Ereignisbenachrichtigungen nur unter bestimmten Bedingungen erforderlich sind, oder wenn eine Anwendung mehrere Ereignishandler bereitstellt und über die Laufzeitbedingungen definiert wird, welcher davon verwendet wird. Im Beispiel des vorherigen Abschnitts wird das dynamische Hinzufügen eines Ereignishandlers veranschaulicht. Weitere Informationen finden Sie unter Ereignisse (Visual Basic) und Ereignisse (C#-Programmierhandbuch).

Auslösen mehrerer Ereignisse

Wenn von der Klasse mehrere Ereignisse ausgelöst werden, generiert der Compiler ein Feld pro Ereignisdelegatinstanz. Ist die Anzahl der Ereignisse sehr hoch, wird u. U. zu viel Speicher beansprucht, wenn für jeden Delegaten ein Feld generiert wird. Für solche Situationen werden in .NET Ereigniseigenschaften bereitgestellt, die Sie mit einer weiteren beliebig wählbaren Datenstruktur zum Speichern von Ereignisdelegaten verwenden können.

Ereigniseigenschaften bestehen aus Ereignisdeklarationen, die von Ereignisaccessoren begleitet werden. Ereignisaccessoren sind die von Ihnen definierten Methoden, mit denen Ereignisdelegatinstanzen der Speicherdatenstruktur hinzugefügt oder daraus entfernt werden können. Beachten Sie, dass Ereigniseigenschaften langsamer als Ereignisfelder sind, da jeder Ereignisdelegat abgerufen werden muss, bevor er aufgerufen werden kann. Sie müssen daher einen Kompromiss zwischen hoher Speicherauslastung und verminderter Geschwindigkeit finden. Wenn die Klasse viele Ereignisse definiert, die selten ausgelöst werden, können Sie Ereigniseigenschaften implementieren. Weitere Informationen finden Sie unter Vorgehensweise: Behandeln mehrerer Ereignisse mit Ereigniseigenschaften.

Titel Beschreibung
How to: Auslösen und Behandeln von Ereignissen In diesem Abschnitt sind Beispiele zum Auslösen und Verarbeiten von Ereignissen enthalten.
How to: Behandeln mehrerer Ereignisse mit Ereigniseigenschaften In diesem Abschnitt wird die Verwendung von Ereigniseigenschaften zum Behandeln mehrerer Ereignisse veranschaulicht.
Beobachterentwurfsmuster Das Entwurfsmuster, mit dem sich ein Abonnent bei einem Anbieter registrieren und Benachrichtigungen von diesem empfangen kann, wird beschrieben.

Siehe auch