CodeGym /Kurse /C# SELF /Standard-Event-Template ( E...

Standard-Event-Template ( EventHandler/ EventArgs)

C# SELF
Level 52 , Lektion 3
Verfügbar

1. Einführung

Erinnern wir uns: ein Event ist ein öffentliches Versprechen deines Codes, die Abonnenten zu "rufen", wenn etwas Wichtiges passiert. In Beispielen aus früheren Vorlesungen könntest du solche Deklarationen gesehen haben:


public event Action<string> MessageSent;

Oder sogar:


public delegate void MyHandler(int value);
public event MyHandler SomethingHappened;

Das funktioniert, aber dieser Ansatz bringt eine Menge Probleme mit sich — angefangen bei inkonsistenten Signaturen bis hin zur Unmöglichkeit zu wissen, wer das Event ausgelöst hat und was genau passiert ist. Stell dir vor, eine Taste auf der Tastatur würde bei jedem Druck zufällig etwas anderes tun — jedes Mal anders, obwohl es dieselbe Anwendung und dieselbe Aufgabe ist. Du spielst ein Spiel und drückst die Leertaste. Vorhin stand sie für Springen, jetzt öffnet sie plötzlich das Inventar. Albtraum, kein Standard!

In .NET gibt es so etwas wie eine Verfassung für Events — eine Konvention für die Handler-Signatur und die Struktur der mit dem Event übergebenen Informationen. Der kanonische Stil sieht so aus:


void Handler(object sender, EventArgs args);

Klingt vertraut? Man findet diese Zeile überall: von Button-Clicks in WinForms bis zu Systemevents in ASP.NET und selbst in Third-Party-Bibliotheken.

2. Was sind EventHandler und EventArgs

Die Hauptidee: jedes Event übermittelt zwei Dinge:

  • Wer hat das Event ausgelöst? sender
  • Was ist passiert? EventArgs — zusätzliche Daten

In der C#-Welt wird das so formuliert:


public delegate void EventHandler(object sender, EventArgs e);
  • object sender — Referenz auf den Initiator des Events. Das kann alles sein — typischerweise einfach this.
  • EventArgs e — ein Objekt mit zusätzlichen Informationen zum Event. Für einfache Fälle verwendet man EventArgs.Empty, für komplexere Fälle eigene Ableitungen von EventArgs.

Lustige Tatsache

In den offiziellen .NET-Empfehlungen für öffentliche APIs ist es üblich, dass ein Event die Signatur (object sender, EventArgs e) hat. Wenn du ein Event ohne sender und EventArgs siehst, dann ist das wahrscheinlich eine vereinfachte, autorenspezifische Version und nicht der kanonische .NET-Stil.

Großer Nutzen eines einheitlichen Standards

Mit einem einheitlichen Standard sind Events einfacher zu loggen, zu testen, universell anzubinden und das System zu erweitern. Öffne den Quellcode von WinForms, WPF oder ASP.NET — du siehst immer dasselbe Muster.

3. Wie man das Standard-Event-Template verwendet

1. Benutze den eingebauten Delegaten EventHandler

Statt einen eigenen Delegaten zu deklarieren, kannst du den fertigen verwenden:


public event EventHandler SmthHappened;

Der Handler sieht jetzt immer gleich aus:


private void OnSmthHappened(object sender, EventArgs e)
{
    // Logik zur Reaktion auf das Event
}

Das Abonnieren bleibt einfach:


myObj.SmthHappened += OnSmthHappened;

Wichtig: Wenn das Event keine zusätzlichen Daten übergibt, verwende EventArgs.Empty.

2. Eigene Event-Argumente erstellen

Wenn du Informationen übergeben musst (Ergebnis einer Berechnung, Dateiname, Fehler), erstelle eine Ableitung von EventArgs:


public class CalculationEventArgs : EventArgs
{
    public double Result { get; }
    public CalculationEventArgs(double result) => Result = result;
}

Dann nutzt du den generischen Delegaten — EventHandler<TEventArgs>:


public event EventHandler<CalculationEventArgs> CalculationFinished;

Und der Handler nimmt nun genau deinen Argumenttyp:


private void OnCalculationFinished(object sender, CalculationEventArgs e)
{
    Console.WriteLine($"Berechnung abgeschlossen. Ergebnis: {e.Result}");
}

4. Beispielanwendung

Wir entwickeln unser Lernprojekt weiter — nehmen wir an, wir haben einen Taschenrechner, der Zahlen addiert oder subtrahiert und das Ende der Operation über ein Event meldet.

Minimalistisches Beispiel


public class Calculator
{
    public event EventHandler<CalculationEventArgs> CalculationPerformed;

    public void Add(int a, int b)
    {
        int result = a + b;
        // "Feuern" des Events
        CalculationPerformed?.Invoke(this, new CalculationEventArgs(result));
    }
}

public class CalculationEventArgs : EventArgs
{
    public int Result { get; }
    public CalculationEventArgs(int result) => Result = result;
}

Abonnieren und verwenden:


var calc = new Calculator();
calc.CalculationPerformed += (sender, e) =>
{
    Console.WriteLine($"Ergebnis der Operation: {e.Result}");
};
calc.Add(10, 20);
// Gibt aus: Ergebnis der Operation: 30

Das ist der ganze "Standard": der Handler nimmt immer den Sender und ein Argumentobjekt — universell und verständlich.

5. Nützliche Feinheiten

Encapsulation des Event-Aufrufs: guter Stil

In .NET ist es üblich, die Logik zum Auslösen des Events in eine geschützte Methode mit dem Präfix On auszulagern:


protected virtual void OnCalculationPerformed(CalculationEventArgs e)
{
    CalculationPerformed?.Invoke(this, e);
}

Und innerhalb der Logik (z.B. "Add", "Subtract" usw.) rufst du einfach diese Methode auf:


public void Add(int a, int b) => OnCalculationPerformed(new CalculationEventArgs(a + b));

Dieser Stil erlaubt es Ableitungen, das Verhalten des Events zu überschreiben und verringert die Chance, das Event zu vergessen.

Diagramm: Wie ein Event nach dem .NET-Standard aufgebaut ist

graph LR
    A[Publizierendes Objekt] -- "event EventHandler/ EventHandler<TEventArgs>" --> B[Liste der Abonnenten]
    B -- "Handler-Methode (object sender, EventArgs e)" --> C[Reaktion auf das Event]
    A -- "this (Sender)" --> C
    A -- "EventArgs (Daten)" --> C

Vergleich der Varianten zur Deklaration von Events

Ansatz Übermittlung des Initiators (sender) Übermittlung von Argumenten Universalität Verwendung in .NET
public event Action<int> MyEvent;
Nein Ja Niedrig Nein
public delegate void MyHandler(object, int); event MyHandler ...
Ja Ja Mittel Nein (selten)
public event EventHandler;
Ja Nein (EventArgs) Hoch Ja, Standard
public event EventHandler<MyArgs>;
Ja Ja (MyArgs) Sehr hoch Ja, Standard

Praktischer Nutzen bei Interviews und in Produktion

Die Verwendung des Standard-Event-Templates ist ein Muss für .NET-Entwickler. Bei einem Interview wird man dich höchstwahrscheinlich nach EventHandler und dem Paar sender/EventArgs fragen. Ein Event vom Typ Action<T> wird oft als "Vereinfachung" wahrgenommen.

In echten Projekten vereinfacht dieser Ansatz die Zusammenarbeit, Tests, Erweiterbarkeit und Wartbarkeit des Codes. Drittanbieter-Bibliotheken (Logging, Profiler) lassen sich leichter integrieren, wenn die Standardform genutzt wird.

Flussdiagramm des Event-Aufrufs

flowchart TD
    subgraph A[Publisher-Klasse]
        C1((Objekt))
        C2[Methode, die das Event auslöst]
        C3["event EventHandler<MyArgs>"]
    end
    subgraph B[Subscriber-Klasse]
        D1((Subscription))
        D2["Event-Handler (object sender, MyArgs args)"]
    end
    C1 -- Ruft auf --> C2
    C2 -- "Invoke(this, args)" --> C3
    C3 -- "Benachrichtigt" --> D1
    D1 -- "Ruft auf" --> D2

6. Tipps, Feinheiten und typische Fehler

1. Handler-Signatur. Du willst ein Event vom Typ Action<int> machen? Verlockend, aber dann verlierst du sender und die Kompatibilität mit dem Ökosystem.

2. Übergabe von Argumenten. Verwechsle EventArgs nicht mit normalen Parametern. Alle Daten für den Handler sollten im Argumentobjekt übergeben werden.

3. Verwendung von null für Argumente. Statt null verwende EventArgs.Empty, wenn keine zusätzlichen Daten vorliegen.

4. Schwache Typisierung. Mache nicht ein "allesfressendes" Event EventHandler und stecke alles hinein. Erstelle für jedes Event eine eigene abgeleitete Klasse — Lesbarkeit und Zuverlässigkeit steigen.

5. Fehler beim Auslösen des Events. Prüfe immer auf Abonnenten: SomeEvent?.Invoke(this, e). Ohne Abonnenten ist die Event-Referenz null.

6. Verletzung der Kapselung. Rufe das Event nicht von außerhalb der Publisher-Klasse auf. Events sind nur für An-/Abmeldung; das Auslösen erfolgt intern über eine On...-Methode.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION