Files
2026-04-17 14:55:32 -04:00

73 lines
5.2 KiB
Markdown

---
source_files:
- Common/DTS.Common.Core/EventManager/EventManager.cs
generated_at: "2026-04-16T11:42:05.172295+00:00"
model: "zai-org/GLM-5-FP8"
schema_version: 1
sha256: "59cbfaadaa1e572f"
---
# Documentation: DTS.Common.Core.EventManager
## 1. Purpose
The `EventManager` class provides a static, global implementation of the Publish/Subscribe (Pub/Sub) pattern. It enables decoupled communication between system components, allowing publishers to emit events without knowledge of subscribers, and subscribers to react to events without direct coupling to publishers. Additionally, it provides a diagnostic infrastructure to monitor the event system's activity, such as tracking subscription counts and event publication flow.
## 2. Public Interface
### Delegates
* **`SubscriberCallbackDelegate<in T>(T item)`**
* The signature required for methods subscribing to events.
* Constraint: `T` must be a reference type (`class`).
* **`DiagnosticCallbackDelegate(EventDiagnosticType eventType, Type t, object eventData, string listener)`**
* The signature required for methods subscribing to diagnostic events regarding the EventManager's internal operations.
### Static Class: `EventManager`
#### Methods
* **`void Publish(T eventData) where T : class`**
* Publishes an event of type `T` to all registered subscribers.
* Iterates through the subscriber list for type `T`. If a subscriber has an `EventFilter`, the filter is evaluated; the callback is invoked only if the filter returns `true`.
* If no subscribers exist for type `T`, the method returns immediately without throwing an exception.
* **`void Subscribe(SubscriberCallbackDelegate listener) where T : class`**
* Registers a listener for event type `T` without a filter.
* Overload: `void Subscribe(SubscriberCallbackDelegate listener, Predicate eventFilter) where T : class`
* Registers a listener with a predicate filter. The callback will only be invoked if `eventFilter(eventData)` returns `true`.
* **`void UnSubscribe(SubscriberCallbackDelegate listener) where T : class`**
* Removes a specific listener from the subscriber list for type `T`.
* Uses the `Callback` delegate equality to find and remove the matching `EventMetaData`.
* **`void Clear()`**
* Removes all listeners for all event types from the `SubscriberList`.
* **`void SubscribeToDiagnosticEvents(DiagnosticCallbackDelegate listener)`**
* Registers a listener to receive diagnostic events (e.g., when items are added or removed).
* **`void UnSubscribeToDiagnosticEvents(DiagnosticCallbackDelegate listener)`**
* Removes a specific listener from the diagnostic events list.
* **`void ClearDiagnosticEvents()`**
* Removes all listeners from the diagnostic events list.
### Enum: `EventDiagnosticType`
Defines the types of diagnostic events generated by the system.
* `AddListener` (0)
* `AddListenerDiagnostic` (1)
* `PublishEvent` (2)
* `RemoveListenerDiagnostic` (3)
* `RemoveListener` (4)
## 3. Invariants
* **Type Constraint:** All event types `T` used in `Publish`, `Subscribe`, and `UnSubscribe` must be reference types (`class`).
* **Thread Safety:** The source code does not contain any locking mechanisms (e.g., `lock`, `Monitor`) around the static `Dictionary` or `List` collections. The module assumes a single-threaded execution context or requires external synchronization by the caller.
* **Reference Equality:** Unsubscription relies on delegate reference equality. A subscriber must pass the exact same delegate instance used during `Subscribe` to successfully `UnSubscribe`.
* **Internal Storage:** Listeners are stored internally as `EventMetaData` objects within a `List<object>`, despite the dictionary value type being `List<object>`.
## 4. Dependencies
* **Internal Dependencies:**
* `System.Collections.Generic` (for `Dictionary`, `List`, `Predicate`)
* `System.Reflection` (for `MemberInfo`, `Type` used in diagnostics)
* **External Dependencies:**
* None identified from the source file alone. As a core utility, it is likely widely depended upon by higher-level application modules.
## 5. Gotchas
* **Thread Safety Risk:** The `SubscriberList` and `DiagnosticList` are static and mutable. Concurrent calls to `Subscribe`/`Publish` from different threads will cause race conditions and likely throw exceptions (e.g., `InvalidOperationException` on the list enumerator during `Publish`).
* **Diagnostic "Clear" Behavior:** The `ClearDiagnosticEvents()` method clears the `DiagnosticList` *before* sending the final `RemoveListenerDiagnostic` event. Consequently, the final diagnostic event is sent to an empty list and will effectively be lost/ignored.
* **Diagnostic Noise during Publish:** The `Publish` method sends a `PublishEvent` diagnostic message *inside* the loop for every single subscriber. If one event has 10 subscribers, 10 diagnostic events are fired for a single `Publish` call.
* **Memory Leaks:** Because `SubscriberList` is static and holds references to delegate targets, failing to call `UnSubscribe` will prevent the subscriber objects from being garbage collected.
* **Silent Failures:** `Publish` silently ignores the event if no subscribers are registered. `UnSubscribe` silently does nothing if the listener is not found.