1. Einführung
Beim Arbeiten mit Programmen stößt du unweigerlich auf Situationen, in denen ein Teil andere darüber informieren muss, dass etwas Wichtiges passiert ist. Ein klassisches Beispiel — der Benutzer hat auf die Maus geklickt, und dieses Event muss verarbeitet werden. Im Alltag leben wir ohnehin in einer Welt von Events: der Wasserkocher pfeift in der Küche — du hörst das Signal und schaltest die Platte aus. Kaffee landet auf der Tastatur — dein Herz macht einen Satz — und du rennst, um das Notebook zu retten. Programmierung folgt denselben Prinzipien.
Event ist ein Mechanismus, der einem Quellobjekt (Publisher) erlaubt, andere Objekte (Subscriber) über Änderungen oder Aktionen zu benachrichtigen. Das ist so etwas wie „ich habe Bescheid gesagt — wer zugehört hat, reagiert“.
In C# sind Events spezielle Konstrukte, die auf einem Delegate-Typ basieren. Ein Delegate definiert die Signatur des Callbacks — also was und wie bei den Subscriber aufgerufen wird. Die Deklaration eines Events erfolgt mit dem Schlüsselwort event, ein Delegate mit delegate.
Wozu Events?
- Lose Kopplung: Der Publisher weiss nichts über die Subscriber — er sendet nur das Signal.
- Architektur-Flexibilität: Man kann Handler dynamisch hinzufügen und entfernen, ohne den Publisher-Code zu ändern.
- Skalierbarkeit: Einen neuen Subscriber hinzugefügt — und er erhält sofort Benachrichtigungen.
Klassisches „Publisher-Subscriber“-Muster
Stell dir vor, wir haben eine Klasse „Brandmelder“ (Publisher) und die Klasse „Person im Gebäude“ (Subscriber). Wenn der Alarm angeht, sendet er das Signal an alle gleichzeitig — egal, wie viele Leute im Gebäude sind oder wo sie sich befinden. Das ist das Publisher-Subscriber- bzw. Observer-Muster.
Der Publisher weiss nicht, wie viele oder welche Subscriber es gibt — er benachrichtigt einfach, und die anderen abonnieren die Benachrichtigung oder ignorieren sie.
Wie funktioniert das in C#?
- Publisher: definiert ein Event (event), stellt Subscription/Unsubscription zur Verfügung.
- Subscriber: subscribed auf das Event und implementiert den Handler (die Handler-Methode wird beim Eintreten des Events aufgerufen).
2. Events in der Praxis: erstes Beispiel
Vom Haus zum Code: wir beschreiben ein einfaches Modell. Angenommen, wir haben eine Konsolen-Anwendung, in der ein Timer-Objekt jede Sekunde „tickt“ und verschiedene Handler reagieren (z. B. geben „Tick“ in der Konsole aus oder zählen die Anzahl der Ticks).
Schritt 1. Delegate und Event definieren
public class SimpleTimer
{
// Deklariere den Delegate für das Event
public delegate void TickEventHandler(object sender, EventArgs e);
// Event, basierend auf dem Delegate
public event TickEventHandler Tick;
public void Start(int count)
{
for (int i = 0; i < count; i++)
{
System.Threading.Thread.Sleep(1000); // Nachahmung eines Ticks!
OnTick(); // Event auslösen!
}
}
protected virtual void OnTick()
{
// Rufe das Event auf, wenn es Subscriber gibt (Tick != null)
Tick?.Invoke(this, EventArgs.Empty);
}
}
Was passiert hier?
- Ein Delegate TickEventHandler mit klassischer Signatur object sender, EventArgs e wurde definiert.
- Das Event Tick ist der Subscription-Punkt für Handler.
- Die Methode Start simuliert das „Ticken“ und ruft periodisch OnTick auf.
- In OnTick wird das Event sicher aufgerufen: Tick?.Invoke(..., EventArgs.Empty).
Schritt 2. Subscriber auf das Event registrieren
class Program
{
static void Main()
{
var timer = new SimpleTimer();
// Subscriben auf das Event Tick
timer.Tick += Timer_Tick;
timer.Start(3);
// Man kann sich auch wieder abmelden, falls nötig
timer.Tick -= Timer_Tick;
}
static void Timer_Tick(object sender, EventArgs e)
{
Console.WriteLine("Tick!");
}
}
Wir erstellen den Timer, subscriben mit dem Operator +=, und bei jedem Tick wird der Handler aufgerufen. Unsubscriben — Operator -=.
3. Nützliche Feinheiten
Warum sind Events besser als „harte“ Aufrufe?
Würde SimpleTimer in OnTick direkt in die Konsole schreiben, wäre die Klasse fest an diese Aktion gebunden. Events befreien den Code: der Timer weiss nicht, was die Subscriber tun — eine Rakete zünden, in eine Datei loggen oder eine E-Mail senden.
Wichtiger Unterschied zwischen Delegates und Events
- Delegate ist ein „Zeiger“ auf eine Methode, während ein Event ein Delegate mit eingeschränktem Zugriff ist.
- Subscriber können nur subscriben/unsubscriben; von außen kann das Event nicht aufgerufen werden — das kann nur der Publisher.
- Um ein Event zu deklarieren, füge den Modifier event zum Delegate-Typ hinzu — der Compiler stellt ein korrektes Zugriffsmodell sicher.
Kurzes Ablaufdiagramm eines Events in C#
+------------------+ +------------------------------+
| | | |
| Publisher | <------> | Subscriber |
| (Publisher/Event)| | (Subscriber/Handler) |
| | | |
+------------------+ +------------------------------+
| 1) deklariert Event | 2) subscribed darauf
| 3) löst es aus | 4) implementiert Handler
Wann sollte man Events verwenden?
- Wenn man eine unbekannte Anzahl von Zuhörern über etwas informieren muss.
- Wenn man die Logik innerhalb der Quellklasse nicht koppeln möchte.
- UI, asynchrone Interaktionen, Systembenachrichtigungen — alles dreht sich um Events.
Kurze Implementierungsbesonderheiten von Events in C#
- Ein Event kann nicht von „außen“ ausgelöst werden: nur der Publisher-Code darf Invoke aufrufen.
- Subscription/Unsubscription: Operatoren +=/-=; es können mehrere Handler existieren.
- Ein Event ist im Grunde eine Liste von Delegates: beim Eintreten werden alle Handler in der Reihenfolge der Subscription aufgerufen.
- Empfohlene Delegates: Verwende EventHandler und EventHandler<TEventArgs> für Kompatibilität mit dem .NET-Ökosystem.
4. Typische Anfängerfehler
Man vergisst zu prüfen, ob es Subscriber gibt: Tick != null. Besser ist der sichere Aufruf: Tick?.Invoke(...).
Man subscribed auf ein Event, meldet sich aber nicht ab, wenn der Handler nicht mehr gebraucht wird. Das kann Objekte im Speicher festhalten und zu Leaks führen.
Man versucht, ein Event von einer externen Klasse aus „aufzurufen“ — der Compiler lässt das nicht zu. Du kannst nicht schreiben wie game.GameOver(), wenn das ein Event und keine Methode ist.
Die Signatur des Delegates wird nicht eingehalten. Für Events benutze die Standardtypen EventHandler oder EventHandler<TEventArgs> — so bleibt dein Code kompatibel mit anderen .NET-Bibliotheken.
GO TO FULL VERSION