7.1 KiB
source_files, generated_at, model, schema_version, sha256
| source_files | generated_at | model | schema_version | sha256 | |
|---|---|---|---|---|---|
|
2026-04-16T02:06:02.522135+00:00 | Qwen/Qwen3-Coder-Next-FP8 | 1 | 59cbfaadaa1e572f |
EventManager
Documentation: EventManager Module
1. Purpose
The EventManager module provides a lightweight, type-safe event pub/sub mechanism that decouples event publishers from subscribers. It enables components to publish events of arbitrary types without compile-time knowledge of listeners, and allows subscribers to register callbacks—optionally with predicate-based filters—to receive only relevant events. Diagnostic instrumentation is built-in to track subscription lifecycle events (add, remove, publish) for observability. This module serves as a core infrastructure component for in-process event-driven communication within the DTS system.
2. Public Interface
delegate void SubscriberCallbackDelegate<in T>(T item) where T : class
A generic delegate for event listeners. Receives the event data (item) and returns void. Contravariant in T.
delegate void DiagnosticCallbackDelegate(EventDiagnosticType eventType, Type t, object eventData, string listener)
A delegate for diagnostic event handlers. Invoked on all diagnostic events with:
eventType: the diagnostic action (EventDiagnosticType)t: the event type being subscribed/published/unsubscribed (ornullfor global operations)eventData: the event payload (ornullfor subscription lifecycle events)listener: fully qualified method name and assembly info (e.g.,"MyNamespace.MyClass.MyMethod, MyAssembly, Version=...")
static class EventManager
A static class managing event subscriptions and publishing.
-
static void Publish<T>(T eventData) where T : class
PublisheseventDatato all subscribers registered for typeT. Filters (if any) are applied before invoking callbacks. Diagnostic events are emitted for each callback invocation. -
static void Subscribe<T>(SubscriberCallbackDelegate<T> listener) where T : class
Registerslistenerfor all events of typeT(no filtering). Equivalent toSubscribe(listener, null). -
static void Subscribe<T>(SubscriberCallbackDelegate<T> listener, Predicate<T> eventFilter) where T : class
Registerslistenerfor events of typeT, but only invokes it wheneventFilter(eventData)returnstrue. Internally wraps listener and filter in anEventMetaData<T>instance. -
static void UnSubscribe<T>(SubscriberCallbackDelegate<T> listener) where T : class
Removes all registrations oflistenerfor typeT. Uses reference equality on the delegate. -
static void Clear()
Removes all event subscriptions (all types, all listeners). Emits a diagnostic event withEventDiagnosticType.RemoveListener. -
static void SubscribeToDiagnosticEvents(DiagnosticCallbackDelegate listener)
Registers a diagnostic listener. Invoked on every diagnostic event (including those triggered by diagnostic subscription itself). -
static void UnSubscribeToDiagnosticEvents(DiagnosticCallbackDelegate listener)
Removes a specific diagnostic listener. -
static void ClearDiagnosticEvents()
Removes all diagnostic listeners.
3. Invariants
- Type-based isolation: Subscriptions and publications are strictly keyed by the exact runtime type
T. Subscribing toBaseEventdoes not receive events of typeDerivedEvent : BaseEvent. - Filter semantics: Filters are applied per subscription during
Publish. IfEventFilterisnull, the callback is always invoked. - Reference equality for unsubscription:
UnSubscribe<T>uses==(reference equality) on the delegate instance. Two different delegate instances—even if pointing to the same method—will not be considered equal. - Diagnostic event ordering: Diagnostic events are emitted synchronously and in the order of subscription:
AddListenerwhenSubscribeis calledPublishEventfor each callback invocation duringPublishRemoveListenerwhenUnSubscribeis calledRemoveListener(global) whenClearis called- Diagnostic-specific events (
AddListenerDiagnostic,RemoveListenerDiagnostic) follow the same pattern for diagnostic listeners.
- No null safety for
eventData:Publish<T>does not validateeventDatais non-null. IfeventDataisnull, it is passed as-is to callbacks and filters. - No thread-safety: The module makes no guarantees about concurrent access.
SubscriberListandDiagnosticListare not thread-safe collections.
4. Dependencies
Dependencies of EventManager
System: Core runtime (System,System.Collections.Generic,System.Reflection)- No external libraries: Pure .NET Framework/BCL usage.
Dependencies on EventManager
- Inferred consumers: Any component in the codebase that uses
EventManager.Subscribe,EventManager.Publish, or diagnostic hooks. - No direct reverse dependencies are visible in this file, but the presence of
DiagnosticCallbackDelegateandEventDiagnosticTypesuggests integration with logging, telemetry, or debugging infrastructure (e.g., a diagnostic monitor or test harness).
5. Gotchas
- No deduplication on subscription: Calling
Subscribemultiple times with the same listener and filter results in multiple registrations.UnSubscribemust be called the same number of times to fully remove it. - Filter method reference in diagnostics: The
listenerstring passed toDiagnosticCallbackDelegateincludes the filter method (if present) duringPublishEventdiagnostics—not the callback method. This is becausemetaData.EventFilter?.Methodis used. - Diagnostic listener recursion risk: A diagnostic listener that itself calls
EventManager.PublishorEventManager.Subscribewill trigger nested diagnostic events (e.g., publishing an event while processing a diagnostic event). This is not prevented. Clear()is global and destructive:Clear()removes all subscriptions across all event types. Use with caution in long-lived systems (e.g., during shutdown).EventMetaData<T>is internal: WhileEventManageris public, theEventMetaData<T>class is internal. This means external code cannot introspect or manipulate subscription metadata directly.- No weak references: Subscribers are held via strong references. If a subscriber object is not explicitly
UnSubscribed, it may be kept alive indefinitely (memory leak risk). DiagnosticCallbackDelegatesignature mismatch inClearDiagnosticEvents: WhenUnSubscribeToDiagnosticEventsis called, it passeslistener.MethodtoSendDiagnosticEvent, butlistenermay benullif the listener was already removed. However,UnSubscribeToDiagnosticEventsonly removes existing entries, solisteneris non-null at the time of call. Still,SendDiagnosticEventhandleslistenerMethod == nullgracefully.- No async support: All callbacks are invoked synchronously. Long-running callbacks will block
Publish.