Bitte beachten Sie, dass sich diese Seite derzeit im Aufbau befindet und daher noch
nicht in ihrer endgültigen Form vorliegt.
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 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.