Event Bus und das Observer-Pattern in C# (.NET): Von der Theorie zur Implementierung
\n\nIn der modernen Entwicklung mit C# (.NET) entsteht häufig die Notwendigkeit, die Interaktion zwischen Komponenten eines Systems zu organisieren, während deren Unabhängigkeit erhalten bleibt. Das Observer-Pattern und seine Weiterentwicklung – der Event Bus (Ereignisbus) – sind zentrale Werkzeuge zur Umsetzung loser Kopplung (loose coupling). In diesem Artikel analysieren wir, wie diese Mechanismen funktionieren, und erstellen eine einfache, aber funktionale Implementierung eines Event Bus in C#.
\n\nObserver-Pattern: Der klassische Ansatz
\n\nDas Observer-Pattern definiert eine "Eins-zu-Vielen"-Beziehung zwischen Objekten: Wenn ein Objekt (der Herausgeber) seinen Zustand ändert, werden alle abhängigen Objekte (die Abonnenten) automatisch benachrichtigt. In .NET ist dieses Pattern durch Ereignisse (event) und Delegaten integriert.
Nachteile der klassischen Implementierung:
\n- \n
- Direkte Abhängigkeit des Abonnenten vom Herausgeber (eine Referenz auf das Objekt muss gespeichert werden). \n
- Komplexität der Verwaltung von Abonnements in großen Systemen. \n
- Probleme mit Threads (Mehrthreadfähigkeit erfordert zusätzliche Synchronisation). \n
Event Bus: Zentraler Vermittler
\n\nDer Event Bus löst diese Probleme, indem er als zentraler Vermittler fungiert. Anstatt sich direkt bei einem bestimmten Objekt zu abonnieren, registrieren sich die Komponenten im Ereignisbus. Herausgeber veröffentlichen Ereignisse im Bus, und der Bus verteilt sie an alle Abonnenten. Dies realisiert das Pub/Sub (Publish/Subscribe)-Pattern.
\n\nVorteile des Event Bus:
\n- \n
- Lose Kopplung: Herausgeber und Abonnenten wissen nichts voneinander. \n
- Skalierbarkeit: Neue Abonnenten können einfach hinzugefügt werden. \n
- Vielseitigkeit: Geeignet für GUI (WPF/WinForms), Microservices und Spiele (Unity). \n
Implementierung eines Event Bus in C# (.NET)
\n\nWir erstellen eine grundlegende Implementierung eines Event Bus, die asynchrone Verarbeitung und typisierte Ereignisse unterstützt. Dazu verwenden wir das Interface IEvent und die generische Klasse EventBus.
Schritt 1: Definieren der Basistypen.
\n// Basis-Interface für alle Ereignisse\npublic interface IEvent { }\n\n// Beispielereignis - Benutzernachricht\npublic class UserCreatedEvent : IEvent\n{\n public int UserId { get; }\n public string Email { get; }\n\n public UserCreatedEvent(int userId, string email)\n {\n UserId = userId;\n Email = email;\n }\n}\n\nSchritt 2: Erstellen des Ereignisbusses.
\nusing System;\nusing System.Collections.Concurrent;\nusing System.Threading.Tasks;\n\npublic class EventBus\n{\n // Speicher für Abonnenten: Schlüssel - Ereignistyp, Wert - Liste der Handler\n private readonly ConcurrentDictionary<Type, ConcurrentBag<object>> _subscribers = new();\n\n // Abonnieren eines Ereignisses\n public void Subscribe<TEvent>(Func<TEvent, Task> handler) where TEvent : IEvent\n {\n var eventType = typeof(TEvent);\n var handlers = _subscribers.GetOrAdd(eventType, _ => new ConcurrentBag<object>());\n handlers.Add(handler);\n }\n\n // Abbestellen (vereinfacht - entfernt alle Handler dieses Typs)\n public void Unsubscribe<TEvent>() where TEvent : IEvent\n {\n _subscribers.TryRemove(typeof(TEvent), out _);\n }\n\n // Veröffentlichen eines Ereignisses (asynchron)\n public async Task PublishAsync<TEvent>(TEvent eventData) where TEvent : IEvent\n {\n if (!_subscribers.TryGetValue(typeof(TEvent), out var handlers))\n return;\n\n foreach (var handler in handlers)\n {\n if (handler is Func<TEvent, Task> typedHandler)\n {\n await typedHandler(eventData);\n }\n }\n }\n}\n\nSchritt 3: Verwendung in der Anwendung.
\npublic class Auch in der Bibliothek
Maschinelles Lernen mit C# (.NET): Überblick über die Bibliotheken ML.NET, Accord.NET, TensorFlow.NET
NestJS für JavaScript (Node.js): Komplettleitfaden mit Beispielen
Joi für JavaScript (Node.js): Vollständige Anleitung mit Beispielen