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

如何监听C# EventHandler的创建与销毁以管控高成本操作?

问题描述

我想用C#的EventHandler,只在必要的时候触发事件。现有示例代码如下:

class EventHandlerExample {
    const int Max = 10;

    static void Main() {
        var counter = new EventHandlerExample();
        counter.OnFinished += (_, args) => Console.WriteLine($"More than threshold: {args.Threshold}");
        
        Console.WriteLine("Starting the count...");
        for (int i = 0; i < Max; i++) {
            Console.WriteLine($" - incrementing to {i + 1}");
            counter.Increment();
        }
    }

    private readonly int _threshold = new Random().Next(Max);
    private int _total;

    private void Increment() {
        _total++;
        if (_total >= _threshold) {
            var args = new FinishedEventArgs {
                Threshold = _threshold,
            };
            OnFinished?.Invoke(this, args);
        }
    }

    public event EventHandler<FinishedEventArgs> OnFinished;
}

public class FinishedEventArgs : EventArgs
{
    public int Threshold { get; init; }
}

OnFinished这个EventHandler会在添加第一个监听器时完成初始化,所以示例里它始终非空;如果去掉counter.OnFinished += ...这行,它就一直是null。哪怕执行下面的操作:

EventHandler<FinishedEventArgs> onFinished = (_, args) => Console.WriteLine($"More than threshold: {args.Threshold}");
counter.OnFinished += onFinished;
counter.OnFinished -= onFinished;

OnFinished也会变回null,这点是没问题的。

但我的场景里,触发事件的前置设置(比如示例中Increment()里的相关逻辑)成本极高;而且因为要用Harmony打补丁,我没办法在Increment()里先判断OnFinished是否非空再执行这些高成本设置。同时,我希望所有监听器注销后,能移除这些高成本代码。

我想要一种能监听EventHandler创建和销毁的机制,比如:

OnFinished.OnCreate += SetupHighCostCode;
OnFinished.OnDestruction += TearDownHighCostCode;

这种实现可行吗?或者有没有其他办法?目前我只能手动管理事件监听器列表,在Add()和Remove()时做响应。


解决方案

C#并没有内置的OnCreate/OnDestruction事件来监听EventHandler的生命周期,但你可以通过以下两种方式实现需求,其中自定义事件访问器的方案最简洁且符合标准事件用法:

1. 自定义事件的add/remove访问器(推荐)

C#允许我们重写事件的添加和移除逻辑,不需要手动维护监听器列表——编译器仍会帮我们管理委托字段,但我们可以在add/remove中插入判断逻辑,在第一个监听器添加时初始化高成本资源,最后一个监听器移除时清理资源。

改造后的代码如下:

class EventHandlerExample {
    const int Max = 10;
    // 私有委托字段,替代编译器自动生成的字段
    private EventHandler<FinishedEventArgs> _onFinished;
    // 标记高成本资源是否已初始化
    private bool _highCostResourcesReady = false;
    // 线程安全锁(多线程场景必备)
    private readonly object _eventLock = new object();

    static void Main() {
        var counter = new EventHandlerExample();
        counter.OnFinished += (_, args) => Console.WriteLine($"More than threshold: {args.Threshold}");
        
        Console.WriteLine("Starting the count...");
        for (int i = 0; i < Max; i++) {
            Console.WriteLine($" - incrementing to {i + 1}");
            counter.Increment();
        }

        // 移除监听器,触发资源清理
        var handler = (EventHandler<FinishedEventArgs>)((_, args) => Console.WriteLine($"More than threshold: {args.Threshold}"));
        counter.OnFinished -= handler;
    }

    private readonly int _threshold = new Random().Next(Max);
    private int _total;

    private void Increment() {
        // 只有高成本资源初始化后才执行相关逻辑
        if (_highCostResourcesReady) {
            _total++;
            if (_total >= _threshold) {
                var args = new FinishedEventArgs {
                    Threshold = _threshold,
                };
                // 加锁避免多线程下委托为空的情况
                lock (_eventLock)
                {
                    _onFinished?.Invoke(this, args);
                }
            }
        }
    }

    // 自定义事件的add/remove逻辑
    public event EventHandler<FinishedEventArgs> OnFinished
    {
        add
        {
            lock (_eventLock)
            {
                // 首次添加监听器时初始化高成本资源
                if (_onFinished == null)
                {
                    SetupHighCostResources();
                    _highCostResourcesReady = true;
                }
                _onFinished += value;
            }
        }
        remove
        {
            lock (_eventLock)
            {
                _onFinished -= value;
                // 最后一个监听器移除时清理资源
                if (_onFinished == null)
                {
                    TearDownHighCostResources();
                    _highCostResourcesReady = false;
                }
            }
        }
    }

    private void SetupHighCostResources()
    {
        Console.WriteLine("初始化高成本资源:安装Harmony补丁、加载重型资源等");
        // 这里写你的高成本初始化逻辑
    }

    private void TearDownHighCostResources()
    {
        Console.WriteLine("清理高成本资源:卸载Harmony补丁、释放重型资源等");
        // 这里写你的资源清理逻辑
    }
}

public class FinishedEventArgs : EventArgs
{
    public int Threshold { get; init; }
}

这个方案的优势:

  • 完全兼容标准的+=/-=事件用法,外部调用者无需修改代码
  • 无需手动维护监听器列表,避免自己管理带来的线程安全问题(加锁后可支持多线程)
  • 精准控制高成本资源的生命周期,只在有监听器时才启用相关逻辑

2. 封装自定义事件类(适合复杂场景)

如果你的场景需要让多个外部组件监听事件的创建/销毁,可以封装一个自定义事件类,内部维护监听器列表并暴露对应的生命周期事件:

public class TrackedEvent<TEventArgs> where TEventArgs : EventArgs
{
    private readonly List<EventHandler<TEventArgs>> _listeners = new List<EventHandler<TEventArgs>>();
    private readonly object _lock = new object();

    // 暴露事件生命周期的回调
    public event EventHandler OnFirstListenerAdded;
    public event EventHandler OnLastListenerRemoved;

    public void AddListener(EventHandler<TEventArgs> handler)
    {
        lock (_lock)
        {
            bool wasEmpty = _listeners.Count == 0;
            _listeners.Add(handler);
            if (wasEmpty)
            {
                OnFirstListenerAdded?.Invoke(this, EventArgs.Empty);
            }
        }
    }

    public void RemoveListener(EventHandler<TEventArgs> handler)
    {
        lock (_lock)
        {
            bool removed = _listeners.Remove(handler);
            if (removed && _listeners.Count == 0)
            {
                OnLastListenerRemoved?.Invoke(this, EventArgs.Empty);
            }
        }
    }

    public void Invoke(object sender, TEventArgs args)
    {
        lock (_lock)
        {
            // 遍历副本避免修改列表时的异常
            foreach (var handler in _listeners.ToArray())
            {
                handler(sender, args);
            }
        }
    }
}

在业务类中使用这个自定义事件:

class EventHandlerExample {
    const int Max = 10;
    private readonly TrackedEvent<FinishedEventArgs> _onFinished = new TrackedEvent<FinishedEventArgs>();
    private bool _highCostResourcesReady = false;

    static void Main() {
        var counter = new EventHandlerExample();
        // 监听事件的生命周期
        counter._onFinished.OnFirstListenerAdded += (_, __) => counter.SetupHighCostResources();
        counter._onFinished.OnLastListenerRemoved += (_, __) => counter.TearDownHighCostResources();

        // 添加业务监听器
        counter._onFinished.AddListener((_, args) => Console.WriteLine($"More than threshold: {args.Threshold}"));
        
        Console.WriteLine("Starting the count...");
        for (int i = 0; i < Max; i++) {
            Console.WriteLine($" - incrementing to {i + 1}");
            counter.Increment();
        }

        // 移除业务监听器
        var handler = (EventHandler<FinishedEventArgs>)((_, args) => Console.WriteLine($"More than threshold: {args.Threshold}"));
        counter._onFinished.RemoveListener(handler);
    }

    private readonly int _threshold = new Random().Next(Max);
    private int _total;

    private void Increment() {
        if (_highCostResourcesReady) {
            _total++;
            if (_total >= _threshold) {
                var args = new FinishedEventArgs {
                    Threshold = _threshold,
                };
                _onFinished.Invoke(this, args);
            }
        }
    }

    private void SetupHighCostResources()
    {
        Console.WriteLine("初始化高成本资源");
        _highCostResourcesReady = true;
    }

    private void TearDownHighCostResources()
    {
        Console.WriteLine("清理高成本资源");
        _highCostResourcesReady = false;
    }
}

public class FinishedEventArgs : EventArgs
{
    public int Threshold { get; init; }
}

这个方案适合需要多组件感知事件生命周期的场景,但外部调用者需要使用AddListener/RemoveListener而非标准的事件运算符。


内容的提问来源于stack exchange,提问作者Stefan S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:30:53