You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何跟踪EventHandler列表事件触发状态,触发后续事件且保留取消订阅能力?

解决方案

核心思路是通过订阅包装类跟踪每个事件的触发状态,同时保存用于取消订阅的委托实例——既解决匿名函数无法取消订阅的问题,又实现“所有事件至少触发一次后触发目标事件”的需求。

具体实现代码

// 跟踪单个事件的订阅状态与委托实例
private class EventSubscriptionTracker
{
    // 保存与订阅时完全一致的委托实例,用于后续取消订阅
    public EventHandler SubscriptionHandler { get; }
    public bool HasTriggered { get; private set; }

    public EventSubscriptionTracker(Action onAllEventsTriggered)
    {
        SubscriptionHandler = (sender, e) =>
        {
            if (!HasTriggered)
            {
                HasTriggered = true;
                // 检查所有事件是否都已触发
                if (_trackers.All(t => t.HasTriggered))
                {
                    onAllEventsTriggered?.Invoke();
                }
            }
        };
    }

    // 手动重置触发状态(如需重复检测场景)
    public void ResetTriggerState()
    {
        HasTriggered = false;
    }
}

// 类内成员
private List<EventSubscriptionTracker> _trackers = new List<EventSubscriptionTracker>();
// 所有事件触发完成后要触发的目标事件
public event EventHandler AllEventsCompleted;

// 批量订阅事件并启动跟踪
public void SubscribeAndTrackEvents(List<EventHandler> eventHandlers)
{
    // 先清理之前的订阅与跟踪状态
    UnsubscribeAllTrackedEvents(eventHandlers);

    foreach (var handler in eventHandlers)
    {
        var tracker = new EventSubscriptionTracker(OnAllEventsCompleted);
        // 给目标事件添加包装后的处理委托
        handler += tracker.SubscriptionHandler;
        _trackers.Add(tracker);
    }
}

// 批量取消所有订阅
public void UnsubscribeAllTrackedEvents(List<EventHandler> eventHandlers)
{
    if (_trackers.Count != eventHandlers.Count)
    {
        throw new InvalidOperationException("跟踪器数量与事件列表不匹配");
    }

    for (int i = 0; i < eventHandlers.Count; i++)
    {
        var handler = eventHandlers[i];
        var tracker = _trackers[i];
        // 使用保存的委托实例取消订阅
        handler -= tracker.SubscriptionHandler;
    }

    _trackers.Clear();
}

// 目标事件的触发逻辑
private void OnAllEventsCompleted()
{
    AllEventsCompleted?.Invoke(this, EventArgs.Empty);

    // 若需支持重复检测,可在此重置所有跟踪器状态
    // foreach (var tracker in _trackers) tracker.ResetTriggerState();
}

关键说明

  1. 规避匿名函数局限:EventSubscriptionTracker类中创建的SubscriptionHandler是可复用的委托实例,订阅和取消订阅使用同一个实例,彻底解决匿名函数无法取消订阅的问题。
  2. 精准状态跟踪:每个跟踪器维护独立的HasTriggered标记,仅在首次触发时更新状态,避免重复计数干扰判断。
  3. 批量管理能力:通过_trackers列表统一管理所有事件的跟踪状态,支持批量取消订阅或重置触发状态。
  4. 线程安全优化(可选):如果事件在多线程环境下触发,可将HasTriggered替换为原子操作标记,确保状态更新的线程安全:
    private int _hasTriggeredFlag; // 0=未触发,1=已触发
    public bool HasTriggered => _hasTriggeredFlag == 1;
    
    // 在处理委托中替换原逻辑:
    if (Interlocked.CompareExchange(ref _hasTriggeredFlag, 1, 0) == 0)
    {
        // 执行后续检查逻辑
    }
    

内容的提问来源于stack exchange,提问作者Permafrost

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:13:16