如何引发弱事件?.NET弱事件管理器使用困惑及最简方案求助
关于WPF弱事件模式的困惑与最简实现方案
一、为什么现有弱事件管理器没有引发事件的接口?
PropertyChangedEventManager和泛型WeakEventManager<TEventSource, TEventArgs>的核心作用是管理事件订阅的弱引用关系,避免订阅者因持有事件源的强引用而无法被GC回收,从而防止内存泄漏。它们本身并不负责触发事件——事件的触发逻辑仍然由事件源类自行实现,和普通事件的触发方式完全一致。
这些类被列为弱事件模式的实现方式,是因为它们封装了弱事件模式最核心的“弱引用订阅”逻辑,你只需在订阅事件时通过它们添加处理程序,而非直接订阅事件源的普通事件,就能实现弱引用订阅的效果。
二、最简的弱事件引发与处理方案
方案1:使用泛型WeakEventManager管理自定义事件
无需继承WeakEventManager,直接用它封装自定义事件,让订阅者通过它完成订阅,事件源正常触发事件即可。
示例代码:
// 事件源类 public class CustomEventSource { // 内部普通事件,用于触发逻辑 private event EventHandler<CustomEventArgs>? InternalCustomEvent; // 对外提供弱引用订阅入口 public void AddCustomEventHandler(EventHandler<CustomEventArgs> handler) { WeakEventManager<CustomEventSource, CustomEventArgs>.AddHandler(this, nameof(InternalCustomEvent), handler); } public void RemoveCustomEventHandler(EventHandler<CustomEventArgs> handler) { WeakEventManager<CustomEventSource, CustomEventArgs>.RemoveHandler(this, nameof(InternalCustomEvent), handler); } // 事件触发方法,由事件源自身调用 public void RaiseCustomEvent(string message) { InternalCustomEvent?.Invoke(this, new CustomEventArgs(message)); } } // 自定义事件参数 public class CustomEventArgs : EventArgs { public string Message { get; } public CustomEventArgs(string message) => Message = message; } // 订阅者类 public class Subscriber { public void OnCustomEvent(object? sender, CustomEventArgs e) { Console.WriteLine($"收到事件:{e.Message}"); } } // 使用示例 var source = new CustomEventSource(); var subscriber = new Subscriber(); // 通过WeakEventManager完成弱引用订阅 source.AddCustomEventHandler(subscriber.OnCustomEvent); // 触发事件 source.RaiseCustomEvent("测试弱事件"); // 订阅者置空后可被GC正常回收,不会因事件订阅被强引用滞留 subscriber = null; GC.Collect();
方案2:自行实现极简弱事件(不依赖WPF类)
如果不想依赖WPF的WeakEventManager,可以用WeakReference手动实现轻量弱逻辑:
public class WeakEvent<TEventArgs> where TEventArgs : EventArgs { private readonly List<WeakReference<EventHandler<TEventArgs>>> _handlers = new(); public void AddHandler(EventHandler<TEventArgs> handler) { _handlers.Add(new WeakReference<EventHandler<TEventArgs>>(handler)); } public void RemoveHandler(EventHandler<TEventArgs> handler) { // 清理已回收的引用并移除匹配的处理程序 _handlers.RemoveAll(wr => !wr.TryGetTarget(out var target) || target == handler); } // 事件触发方法,由事件源调用 public void Invoke(object sender, TEventArgs e) { var toRemove = new List<WeakReference<EventHandler<TEventArgs>>>(); foreach (var wr in _handlers) { if (wr.TryGetTarget(out var handler)) { handler.Invoke(sender, e); } else { toRemove.Add(wr); } } // 批量清理无效引用 foreach (var wr in toRemove) _handlers.Remove(wr); } } // 事件源类 public class CustomEventSource { private readonly WeakEvent<CustomEventArgs> _customEvent = new(); // 对外暴露标准事件接口 public event EventHandler<CustomEventArgs> CustomEvent { add => _customEvent.AddHandler(value); remove => _customEvent.RemoveHandler(value); } public void RaiseCustomEvent(string message) { _customEvent.Invoke(this, new CustomEventArgs(message)); } } // 订阅者和使用方式与方案1一致
总结
- 弱事件管理器仅负责维护弱引用订阅关系,事件触发是事件源的职责,这就是它们没有引发事件接口的原因。
- 最简实现优先选择泛型
WeakEventManager封装事件,无需继承,只需通过它的AddHandler/RemoveHandler实现弱引用订阅,事件源正常触发内部事件即可。
内容的提问来源于stack exchange,提问作者Hossein Ebrahimi
相关产品推荐
相关产品推荐

