73 lines
5.2 KiB
Markdown
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.
|