--- 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(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`, despite the dictionary value type being `List`. ## 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.