为何无法为Action创建带ref的this扩展方法?
问题与解决方案
原尝试代码(无法运行)
用户尝试实现一种无需存储委托即可取消订阅的事件模式,但因Action是引用类型,无法作为ref this参数用于扩展方法,代码如下:
public static class Program { public static event Action OnEvent; public static void Main() { var registration = OnEvent.Subscribe(() => { Console.Write("OnEventInvoked"); }); OnEvent?.Invoke(); registration.Dispose(); } public static CancellationTokenRegistration Subscribe(ref this Action action, Action callback) { action += callback; return new CancellationToken().Register(() => { action -= callback; }); } }
需求说明
- 通过
event修饰符禁止在声明类外部调用事件的Invoke方法; - 实现无需存储委托实例即可取消订阅的
Subscribe方法; - 不能创建包含委托的自定义公开类,避免
Invoke方法被外部访问。
可行实现方案
可以通过在事件声明类内部封装订阅逻辑,返回自定义的IDisposable实现来完成需求,既保持事件的封装性,又能便捷取消订阅:
public static class Program { // 私有event确保外部无法直接Invoke或操作委托实例 private static event Action OnEvent; public static void Main() { // 使用using自动释放,无需手动管理Dispose using var registration = Subscribe(() => Console.Write("OnEventInvoked")); OnEvent?.Invoke(); } // 公开的订阅方法,返回IDisposable用于取消订阅 public static IDisposable Subscribe(Action callback) { OnEvent += callback; return new Unsubscriber(() => OnEvent -= callback); } // 私有内部类实现取消订阅逻辑 private sealed class Unsubscriber : IDisposable { private readonly Action _unsubscribeAction; private bool _isDisposed; public Unsubscriber(Action unsubscribeAction) { _unsubscribeAction = unsubscribeAction; } public void Dispose() { if (!_isDisposed) { _unsubscribeAction(); _isDisposed = true; } } } }
方案说明
- 事件封装:将
OnEvent设为私有event,外部完全无法调用Invoke,也不能直接操作委托实例,仅能通过类提供的Subscribe方法订阅; - 取消订阅逻辑:
Subscribe方法返回自定义的Unsubscriber实例,该实例持有移除委托的逻辑,调用Dispose时自动执行取消订阅; - 使用便捷性:可以配合
using语句自动释放,无需手动保存委托实例,满足需求中“无需存储action即可取消订阅”的要求。
内容的提问来源于stack exchange,提问作者gustavopsantos
相关产品推荐
相关产品推荐

