C#委托单次调用自动订阅取消订阅工具函数实现问询
实现C#委托单次触发后自动取消订阅的工具函数
问题背景
需要实现一个工具函数,为委托订阅目标函数,且目标函数在委托首次触发执行后自动取消订阅。适用场景:
- 存在大量频繁触发的委托
- 部分函数仅需在委托首次触发时执行一次
- 目标函数被多个委托复用,无法在函数内部逐个处理取消订阅逻辑
原尝试代码因局部函数无法访问ref参数导致失效——ref修饰的委托参数无法被局部函数/lambda捕获,无法在wrapper中执行取消订阅操作。
解决方案
通过传递订阅/取消订阅的委托逻辑替代ref参数,让lambda捕获外部的委托变量,从而实现wrapper执行时自动取消自身订阅。
基础版实现(无参数委托)
public void UtilitySingleCall(Action<Action> addSubscription, Action<Action> removeSubscription, Action targetFunc) { // 先声明wrapper变量,确保lambda能引用自身 Action wrapper = null; wrapper = () => { // 执行目标函数 targetFunc(); // 取消当前wrapper的订阅 removeSubscription(wrapper); }; // 将wrapper添加到目标委托 addSubscription(wrapper); }
使用示例
// 原有的频繁使用的委托 public Action OnRoundStart; public Action OnRoundEnd; // 为两个委托分别订阅MyFunction,指定各自的订阅/取消逻辑 UtilitySingleCall( handler => OnRoundStart += handler, handler => OnRoundStart -= handler, MyFunction ); UtilitySingleCall( handler => OnRoundEnd += handler, handler => OnRoundEnd -= handler, MyFunction ); // 首次触发:两次调用MyFunction OnRoundStart?.Invoke(); OnRoundEnd?.Invoke(); // 后续触发:无任何调用 OnRoundStart?.Invoke(); OnRoundEnd?.Invoke(); public void MyFunction() { Console.WriteLine("MyFunction"); }
泛型扩展(支持带参数的委托)
如果需要支持带参数的委托(如Action<T>),可以扩展为泛型版本:
public void UtilitySingleCall<T>(Action<Action<T>> addSubscription, Action<Action<T>> removeSubscription, Action<T> targetFunc) { Action<T> wrapper = null; wrapper = (arg) => { targetFunc(arg); removeSubscription(wrapper); }; addSubscription(wrapper); }
泛型版使用示例
public Action<string> OnMessageReceived; // 订阅带参数的目标逻辑 UtilitySingleCall( handler => OnMessageReceived += handler, handler => OnMessageReceived -= handler, message => Console.WriteLine($"Received: {message}") ); OnMessageReceived?.Invoke("Hello"); // 执行一次,输出"Received: Hello" OnMessageReceived?.Invoke("World"); // 无输出
方案优势
- 无需修改原有委托的定义方式
- 目标函数无需添加标志位或额外逻辑
- 支持同一个目标函数被多个委托复用,各自独立管理订阅生命周期
内容的提问来源于stack exchange,提问作者TheGrimalkin
相关产品推荐
相关产品推荐

