C#事件与委托:如何延迟调用事件订阅者并触发后自动移除?
实现延迟触发并自动取消订阅的事件处理程序
我学习了C#的事件与委托,编写了如下基础示例:
public class Subject { public delegate void RandomDelegate(); public event RandomDelegate randomEvent; void CallRandom() { if (randomEvent != null) {randomEvent();} } }
目前可以通过Subject.randomEvent += randomEventHandler;的方式订阅事件,处理每次触发逻辑。但我需要实现一种延迟事件处理程序:订阅后,仅在第n次触发randomEvent时执行目标逻辑,且执行完成后自动取消订阅。
我尝试编写了DelayedHandler类,但还存在困惑:
public class DelayedHandler { public int CurrentDelay { get; private set; } = 0; public int Delay { get; init; } public DelayedHandler(int delay) { Delay = delay; } public void Handler() // 调用方式:randomEvent += new DelayedHandler().Handler { if (CurrentDelay >= Delay) { // 待执行的逻辑 } else { CurrentDelay++; } } }
现有代码的问题
当前的DelayedHandler无法自动取消订阅,因为它没有持有事件的引用,也无法在满足触发条件后从事件中移除自身的委托实例。
解决方案
方案1:使用扩展方法+闭包(简洁灵活)
通过扩展方法封装延迟订阅逻辑,利用闭包维护计数和委托引用,实现自动取消订阅:
public static class SubjectEventExtensions { public static void SubscribeDelayed(this Subject subject, int triggerAfter, Action onTriggered) { int triggerCount = 0; RandomDelegate handler = null; handler = () => { triggerCount++; if (triggerCount == triggerAfter) { onTriggered(); // 执行完成后取消订阅 subject.randomEvent -= handler; } }; subject.randomEvent += handler; } }
使用方式:
var subject = new Subject(); // 订阅第3次触发时执行逻辑 subject.SubscribeDelayed(3, () => Console.WriteLine("延迟触发的逻辑执行了"));
方案2:改进DelayedHandler类(封装性强)
让DelayedHandler持有事件源的引用,在满足条件时主动移除自身委托:
public class DelayedHandler { private int _currentTriggerCount = 0; private readonly int _triggerAfter; private readonly Subject _eventSource; private readonly Action _targetAction; public DelayedHandler(Subject eventSource, int triggerAfter, Action targetAction) { _eventSource = eventSource; _triggerAfter = triggerAfter; _targetAction = targetAction; // 初始化时自动订阅事件 _eventSource.randomEvent += HandleEvent; } private void HandleEvent() { _currentTriggerCount++; if (_currentTriggerCount == _triggerAfter) { _targetAction(); // 执行后取消订阅 _eventSource.randomEvent -= HandleEvent; } } }
使用方式:
var subject = new Subject(); // 创建Handler实例,自动订阅第3次触发 new DelayedHandler(subject, 3, () => Console.WriteLine("延迟触发的逻辑执行了"));
两种方案都能实现需求:方案1更轻量化,适合快速调用;方案2将逻辑封装在类中,便于状态管理和复用。
内容的提问来源于stack exchange,提问作者Lnio Yarschov
相关产品推荐
相关产品推荐

