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

5.2 KiB

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
Common/DTS.Common.Core/EventManager/EventManager.cs
2026-04-16T11:42:05.172295+00:00 zai-org/GLM-5-FP8 1 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.