WinUI3中CommunityToolkit.MVVM WeakReferenceMessenger调用已释放对象问题
解决WinUI3中WeakReferenceMessenger引发的ObjectDisposedException问题
你遇到的问题核心在于:WeakReferenceMessenger仅通过弱引用避免订阅者被Messenger强引用导致内存泄漏,但当订阅者(UserControl)已被Disposed但尚未被GC回收时,Messenger仍能通过弱引用获取到实例并调用Receive方法,进而触发已释放对象的访问异常。以下是几种可行的解决方法:
方法1:在UserControl的Dispose阶段主动取消订阅
WinUI3的UserControl实现了IDisposable接口,可通过重写Dispose方法在对象释放时直接取消消息订阅,从根源上避免后续消息触发Receive逻辑。
示例代码:
public partial class YourUserControl : UserControl { private bool _isDisposed; protected override void Dispose(bool disposing) { if (!_isDisposed) { if (disposing) { // 取消对应消息类型的订阅,若使用了token需一并传入 WeakReferenceMessenger.Default.Unregister<YourMessageType>(this); // 若订阅了多种消息,需分别取消 // WeakReferenceMessenger.Default.Unregister<AnotherMessageType>(this); } _isDisposed = true; base.Dispose(disposing); } } // 你的消息接收方法 public void Receive(YourMessageType message) { // 业务逻辑 } }
方法2:在Receive方法中先检查对象释放状态
如果无法在Dispose阶段及时取消订阅(比如存在复杂的生命周期依赖),可在Receive方法开头先判断对象是否已释放,避免访问已释放的资源。
示例代码:
public partial class YourUserControl : UserControl { private bool _isDisposed; protected override void Dispose(bool disposing) { if (!_isDisposed) { if (disposing) { // 其他释放逻辑 } _isDisposed = true; base.Dispose(disposing); } } public void Receive(YourMessageType message) { // 先判断对象是否已释放,若已释放直接返回 if (_isDisposed || !this.IsLoaded) { return; } // 原业务逻辑,确保只在对象有效时执行 // ... } }
方法3:利用控件Unloaded事件辅助取消订阅
结合WinUI控件的Unloaded事件,在控件从视觉树移除时取消订阅,能更精准地匹配控件的可用生命周期:
public YourUserControl() { InitializeComponent(); WeakReferenceMessenger.Default.Register<YourMessageType>(this, Receive); this.Unloaded += (s, e) => WeakReferenceMessenger.Default.Unregister<YourMessageType>(this); }
关键注意事项
- WeakReferenceMessenger的弱引用仅负责避免内存泄漏,不处理对象已Disposed但未被GC回收的场景,这是设计预期,并非bug。
- 若使用了自定义的订阅token,取消订阅时必须传入相同的token,否则无法正确取消。
- 对于WinUI3控件,优先在
Dispose或Unloaded事件中处理订阅取消,这两个时机能更精准地把控控件的生命周期边界。
内容的提问来源于stack exchange,提问作者user1757226
相关产品推荐
相关产品推荐

