ein Projekt von goloroden.de

Ereignisse

Bitte beachten Sie, dass sich diese Seite derzeit im Aufbau befindet und daher noch nicht in ihrer endgültigen Form vorliegt.

Übersicht

Ereignisse definieren

Jedem Ereignis liegt in C# ein Delegat zu Grunde, so dass für jedes Ereignis evaluiert werden muss, welcher Delegat geeignet ist. Da Delegaten, die zwar über den gleichen Rückgabetyp und die gleichen Parametertypen verfügen, aber verschieden benannt wurden, weder implizit noch explizit zueinander konvertierbar sind, empfiehlt es sich, die verwendete Anzahl von Delegaten gering zu halten.

Definieren Sie aus diesem Grund Delegaten, die Ereignissen zu Grunde liegen, immer auf die gleiche Art. Verwenden Sie als Rückgabetyp void, für den ersten Parameter verwenden Sie den Typ object, für den zweiten den Typ EventArgs oder einen davon abgeleiteten Typ.

Um ein Ereignis mit einem Parameter vom Typ EventArgs zu definieren, verwenden Sie den vordefinierten Delegaten EventHandler. Um ein Ereignis mit einem Parameter zu definieren, dessen Typ von EventArgs abgeleitet ist, verwenden Sie den vordefinierten generischen Delegaten EventHandler<T>.

RICHTIG: Verwenden Sie als Delegaten für ein Ereignis den vordefinierten Delegaten EventHandler, oder dessen generische Version EventHandler<T>, falls Sie eine von EventArgs abgeleitete Klasse einsetzen.

Das folgende Beispiel zeigt, wie Sie ein Ereignis mit einem vordefinierten Delegaten korrekt implementieren:
C#
1
2
3
4
5
6
7
8
9
10
using System;

public class Foo
{
    public event EventHandler<FooEventArgs> Updated;
}

public class FooEventArgs : EventArgs
{
}
In C# gibt es zwei verschiedene Möglichkeiten, Ereignisse zu definieren: Auf der einen Seite die üblicherweise verwendete Feldsyntax, auf der anderen Seite die selten genutzte Eigenschaftensyntax.

Wenn ein Typ eine geringe Anzahl an Ereignissen enthält und Sie keinen Einfluss auf das Hinzufügen und Entfernen von ereignisbehandelnden Methoden benötigen, verwenden Sie die Feldsyntax.

RICHTIG: Definieren Sie ein Ereignis mit der Feldsyntax, wenn der das Ereignis definierende Typ nur wenige Ereignisse enthält und Sie keinen Einfluss auf das Hinzufügen und Entfernen von ereignisbehandelnden Methoden benötigen.

Das folgende Beispiel zeigt, wie Sie ein Ereignis in Feldsyntax korrekt definieren:
C#
1
2
3
4
5
6
using System;

public class Foo
{
    public event EventHandler Updated;
}
Um das Hinzufügen und Entfernen von ereignisbehandelnden Methoden und die Zuweisung von Ereignissen an Delegaten innerhalb des ereignisdefinierenden Typs kontrollieren zu können, verwenden Sie die Eigenschaftensyntax.

Ziehen Sie bei einer großen Anzahl von Ereignissen, von denen in der Praxis potenziell allerdings nur wenige gleichzeitig genutzt werden, in Betracht, die ereignisbehandelneden Methoden in einer Liste zu speichern, statt für jedes Ereignis eine eigene Delegatinstanz zu erzeugen.

RICHTIG: Definieren Sie ein Ereignis mit der Eigenschaftensyntax, wenn der das Ereignis definierende Typ zahlreiche Ereignisse enthält und Sie Einfluss auf das Hinzufügen und Entfernen von ereignisbehandelnden Methoden benötigen.

C# verwendet intern beim Einsatz der Feldsyntax eine implizite Sperre, die sicherstellt, dass das Hinzufügen und Entfernen von ereignisbehandelnden Methoden threadsicher ausgeführt wird. Verwenden Sie eine explizite Sperre auf ein privates Sperrobjekt, wenn Sie das Ereignis mit Hilfe der Eigenschaftensyntax definieren. Setzen Sie die Sperre nicht auf das Ereignis.

RICHTIG: Verwenden Sie eine explizite Sperre auf ein privates Sperrobjekt, wenn Sie das Ereignis mit Hilfe der Eigenschaftensyntax definieren.

Das folgende Beispiel zeigt, wie Sie ein Ereignis in Eigenschaftensyntax korrekt definieren:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;

public class Foo
{
    private EventHandler _updated;
    private object _updatedLock = new object();

    public event EventHandler Updated
    {
        add
        {
            lock (this._updatedLock)
            {
                this._updated += value;
            }
        }

        remove
        {
            lock (this._updatedLock)
            {
                this._updated -= value;
            }
        }
    }
}

Ereignisse auslösen

Ereignisse können in C# nur von dem Typ ausgelöst werden, der das Ereignis definiert. Das bedeutet, dass ein Ereignis nicht nur von keinem anderen Typ, sondern auch nicht von einem abgeleiteten Typ ausgelöst werden kann.

Definieren Sie aus diesem Grund eine eigene Methode, die das Ereignis auslöst, und markien Sie diese mit dem Zugriffsmodifizierer protected.

Um einem abgeleiteten Typ außerdem zu ermöglichen, sich an das Auslösen eines Ereignisses anzuhängen oder dieses gegebenenfalls unterdrücken zu können, markieren Sie diese Methode als virtual.

RICHTIG: Definieren Sie eine Methode, die das Ereignis auslöst, und markieren Sie diese als protected virtual.

Um zu verhindern, dass ein Ereignis ausgelöst wird, an das keine ereignisbehandelnden Methoden angehängt wurden, stellen Sie vor dem Auslösen des Ereignisses sicher, dass es nicht dem Literal null entspricht.

RICHTIG: Stellen Sie vor dem Auslösen eines Ereignisses sicher, dass es nicht dem Literal null entspricht.

Um zu verhindern, dass sich die Anzahl der ereignisbehandelnden Methoden zwischen dem Vergleich auf null und dem Auslösen des Ereignisses beispielsweise durch konkurriende Threads verändert, erstellen Sie in der ereignisauslösenden Methode eine zusätzliche Referenz auf das Ereignis und verwenden Sie diese.

Diese Vorgehensweise funktioniert, da das Hinzufügen oder Entfernen einer ereignisbehandelnden Methode einen neuen dem Ereignis zu Grunde liegenden Delegaten erzeugt und den bestehenden, auf den die zusätzliche Referenz verweist, nicht modifiziert.

RICHTIG: Erstellen Sie in der ereignisauslösenden Methode eine zusätzliche Referenz auf das Ereignis und verwenden Sie diese.

Um ereignisbehandelnden Methoden die Möglichkeit zu geben, auf Basis des ereignisauslösenden Objektes unterschiedlich reagieren zu können, übergeben Sie dem Ereignis als ersten Parameter immer eine zu object konvertierte Referenz auf das auslösende Objekt.

Falls das Ereignis mit dem Schlüsselwort static als klassenbezogen markiert wurde, übergeben Sie dem Ereignis als ersten Parameter das Literal null.

RICHTIG: Übergeben Sie dem Ereignis als ersten Parameter immer eine Referenz auf das auslösende Objekt, wenn es sich um ein objektbezogenes Ereignis handelt. Übergeben Sie das Literal null, wenn es sich um ein klassenbezogenes Ereignis handelt.

Um einem Ereignis weitere Daten zu übergeben, erzeugen Sie ein Objekt der Klasse EventArgs oder einer davon abgeleiteten Klasse und übergeben Sie dieses Objekt der ereignisauslösenden Methode. Erzeugen Sie dieses Objekt nicht erst in der ereignisauslösenden Methode.

RICHTIG: Erzeugen Sie ein Objekt der Klasse EventArgs oder einer davon abgeleiteten Klasse in allgemeinem Code und übergeben Sie dieses der ereignisauslösenden Methode.

Falls einem Ereignis keine weiteren Daten übergeben werden sollen, übergeben Sie nicht das Literal null, sondern verwenden Sie statt dessen die Eigenschaft Empty der Klasse EventArgs.

FALSCH: Übergeben Sie dem Ereignis als zweiten Parameter nicht das Literal null, wenn Sie keine weiteren Daten übergeben wollen, sondern verwenden Sie statt dessen die Eigenschaft Empty der Klasse EventArgs.

Das folgende Beispiel zeigt, wie Sie eine ereignisauslösende Methode korrekt implementieren:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;

public class Foo
{
    public event EventHandler Updated;

    protected virtual void OnUpdated()
    {
        EventHandler handler = this.Updated;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}
Beachten Sie, dass unter Umständen eine der ereignisbehandelnden Methoden eine Ausnahme auslösen kann. Wird diese Ausnahme nicht von der ereignisbehandelnden Methode selbst gefangen, wird diese zunächst an die ereignisauslösende Methode und dann an die nächsthöhere Ebene weitergereicht. Die noch nicht aufgerufenen ereignisbehandelnden Methoden werden in diesem Fall nicht mehr ausgeführt.

Entwerfen Sie Ihren Code derart, dass er sich nicht darauf verlässt, dass in jedem Fall alle ereignisbehandelnden Methoden ausgelöst werden. Durchlaufen Sie in der ereignisauslösenden Methode die Liste der ereignisbehandelnden Methoden nicht per Hand und fangen Sie sämtliche Ausnahmen, da dieses Vorgehen den Regeln zum Umgang mit Ausnahmen widerspricht.

FALSCH: Fangen Sie in der ereignisauslösenden Methode keine allgemeinen Ausnahmen, um die Ausführung aller ereignisbehandelnden Methoden sicherzustellen. Behandeln Sie potenzielle Ausnahmen statt dessen in den ereignisbehandelnden Methoden.