CodeGym /Kurse /C# SELF /Empfehlungen zur Organisation von Code

Empfehlungen zur Organisation von Code

C# SELF
Level 54 , Lektion 1
Verfügbar

1. Stil der Deklaration und Benennung von Events

Events sind nicht einfach Delegates. Das ist eine eigene Entität zur Kommunikation zwischen Teilen der Anwendung, und ihre Deklaration sollte verständlich sein.

Verwende den richtigen Delegatetyp

In 99% der Fälle verwende die Standard-Delegates:

  • EventHandler — für Events ohne Daten.
  • EventHandler<TEventArgs> — wenn Parameter übergeben werden müssen.

Standardisierung erleichtert Wartung und Integration mit .NET-Bibliotheken. Erfinde keinen eigenen Delegate, wenn EventHandler passt.


public event EventHandler SomethingHappened; // Keine Daten
public event EventHandler<MyEventArgs> DataReceived; // Zusätzliche Daten vorhanden

Wenn spezielle Customization nötig ist — deklariere einen eigenen Delegate, aber das ist selten.

Namen von Events

In .NET werden Events im Perfekt benannt: Completed, Clicked, Changed, Received. Das betont, dass etwas bereits passiert ist.

Beispiele:


public event EventHandler DataLoaded;    // Daten wurden geladen
public event EventHandler<MessageEventArgs> MessageReceived; // Nachricht empfangen
public event EventHandler Saving;        // Der Speichervorgang hat begonnen

Manchmal wird die Form Changing für "vorher"-Events verwendet, um Eingreifen zu ermöglichen.

2. Organisation der Publisher-Klasse: protected virtual Methode OnEvent

Füge immer eine geschützte virtuelle Methode hinzu, die das Event auslöst: zentraler Aufrufpunkt, Erweiterbarkeit durch Vererbung und vorhersehbares Verhalten.


public class FileLoader
{
    public event EventHandler<FileLoadedEventArgs> FileLoaded;

    protected virtual void OnFileLoaded(FileLoadedEventArgs e)
    {
        FileLoaded?.Invoke(this, e);
    }

    public void Load(string filename)
    {
        // ... Logik zum Laden der Datei ...
        OnFileLoaded(new FileLoadedEventArgs(filename));
    }
}

public class FileLoadedEventArgs : EventArgs
{
    public string FileName { get; }
    public FileLoadedEventArgs(string fileName) => FileName = fileName;
}

Lass nur OnFileLoaded das Event auslösen — so ist es leichter zu warten und zu testen.

3. Regeln für Subscribe und Unsubscribe: Lebenszyklus, IDisposable

Wenn die Lebenszeit des Subscribers kürzer ist als die des Publishers, unsubscribe unbedingt vor der Zerstörung des Subscribers. Praktisch ist die Implementierung von IDisposable und das Abmelden in Dispose().


public class TemporaryListener : IDisposable
{
    private readonly Publisher _publisher;

    public TemporaryListener(Publisher publisher)
    {
        _publisher = publisher;
        _publisher.DataReceived += HandleData;
    }

    private void HandleData(object sender, EventArgs e)
    {
        // Verarbeitung der Daten
    }

    public void Dispose()
    {
        _publisher.DataReceived -= HandleData;
    }
}

// Verwendung mit using:
using (var listener = new TemporaryListener(myPublisher))
{
    // listener hört hier Events
}
// Nach dem Verlassen des using - Dispose wurde aufgerufen, Unsubscribe ist erfolgt

Wenn man das Unsubscribe vergisst, hält der Publisher eine Referenz auf den Delegate des Subscribers — Memory Leak und "Zombie-Objekte" sind die Folge.

4. Thread-sicherer Aufruf von Events

In multithreaded Code können Subscriber parallel zum Auslösen des Events hinzugefügt/entfernt werden. Das führt zu Rennen und zu NullReferenceException. Verwende das threadsichere Pattern: kopiere den Delegate in eine lokale Variable.


protected virtual void OnSomethingHappened()
{
    EventHandler handler = SomethingHappened;
    handler?.Invoke(this, EventArgs.Empty);
}

Ab C# 6+ reicht:


SomethingHappened?.Invoke(this, EventArgs.Empty);

5. Verwende EventArgs statt object

Übergib keine Daten über object oder Klassenfelder. Nutze starke Typisierung durch Unterklassen von EventArgs.


public class DownloadCompletedEventArgs : EventArgs
{
    public string FileName { get; }
    public long Size { get; }
    public DownloadCompletedEventArgs(string fileName, long size)
    {
        FileName = fileName;
        Size = size;
    }
}

public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted;

6. Dokumentation von Events und Subscribern

Dokumentiere: wann das Event ausgelöst wird, Bedeutung der Felder in EventArgs, ob und wann ein Unsubscribe nötig ist.


/// <summary>
/// Das Event tritt nach erfolgreichem Laden der Daten auf.
/// </summary>
public event EventHandler<DataLoadedEventArgs> DataLoaded;

7. Zusammenfassende Empfehlungen zur Event-Architektur

Trenne Verantwortlichkeiten

Der Publisher benachrichtigt nur über das Ereignis. Der Subscriber entscheidet selbst, wann er subscribe/ unsubscribe.

Vermeide "Spam" von Events

Erzeuge nicht dasselbe Event dutzende Male pro Sekunde ohne Grund — das belastet unnötig.

Vermeide Events für bidirektionale Kommunikation

Events sind für "one-to-many" geeignet. Für bidirektionale Kommunikation erwäge Interfaces, Callbacks oder andere Mechanismen.

Halte keine direkten Referenzen auf Subscriber in der Klasse

Behalte keine expliziten Referenzen auf Subscriber — Events und Delegates übernehmen das automatisch.

8. Klassische Antipatterns

Typlose Events


public event Action<object> SomethingHappened; // Unklar, was drinsteckt

Schlecht: Typensicherheit ist gebrochen, man braucht Casts, Wartbarkeit leidet.

Unsubscribe vergessen


public class ShortLivedListener
{
    public ShortLivedListener(Publisher p) =>
        p.DataReceived += DoWork;

    private void DoWork(object sender, EventArgs e) { /* ... */ }
    // Kein Dispose, kein Unsubscribe => Zombie-Objekte!
}

Verletzung des SRP

Eine Klasse ist gleichzeitig Publisher, Subscriber und Handler — Rollen vermischen. Trenne Verantwortlichkeiten.

9. Praktische Anwendung in Interviews und Projekten

In vielen Projekten mit Publish-Subscribe ist eine saubere Event-Organisation entscheidend für Skalierbarkeit und Wartbarkeit. In Interviews wird oft verlangt:

  • ein Event-System mit korrekter Typisierung zu implementieren,
  • das Management des Lebenszyklus von Subscribers zu demonstrieren,
  • den threadsicheren Aufruf von Events zu erklären.

Sauberer, dokumentierter, korrekt organisierter Event-Code hebt dich bei Bewerbungen hervor.

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