ViewModel 是否必须取消订阅 ViewModel 中实例化的对象的事件?

问题描述 投票:0回答:2

我将 WPF 与 MVVM 结合使用。我有一个

ViewModel
将对象
MyService
实例化为属性。
ViewModel
订阅
MyService
的事件。
MyService
属性绑定到
View
中的某些元素。

当不再使用

ViewModel
时,由于事件订阅,
MyService
会保持我的
ViewModel
处于活动状态并防止垃圾收集 (GC) 吗?
如果是,是否有简单的方法来解决此问题?我应该在哪里取消订阅
MyService
? (尽管我无法控制调用我的 View/Viewmodel 的人)

public class ViewModel 
{
    public MyService MyService { get; set; } = new MyService();
    
    public ViewModel()
    {
        MyService.MyEvent += OnMyEvent;
    } 

    private void OnMyEvent(object sender, EventArgs e)
    {
        // do something
    }
}
c# wpf events mvvm
2个回答
3
投票

一般来说,您应该始终取消订阅事件,最好是在事件处理程序中取消订阅。

public void DownloadFile()
{
  this.ServiceClient.DownloadCompleted += OnDownloadCompleted;
}

public void OnDownloadCompleted(object sender, EventArgs e)
{
  this.ServiceClient.DownloadCompleted -= OnDownloadCompleted;

  // Do something
}

在您不知道事件源生命周期的情况下,请使用弱事件模式或

IDisposable
模式(但弱事件模式应该是首选)。

要实现弱事件模式,您可以尝试使用现有的

WeakEventManager
实现(例如
PropertyChangedEventManager
)。或者,如果不存在,您可以使用通用的
WeakEventManager<T>
。由于此类使用反射来解析和订阅事件委托,因此建议扩展抽象类
WeakEventManager
来创建自定义类型。
请参阅 Microsoft 文档:弱事件模式

public MyService MyService { get; set; } = new MyService();

public ViewModel()
{
  // MyService.MyEvent += OnMyEvent;

  WeakEventManager<MyService, EventArgs>.AddHandler(
    this.MyService,
    nameof(MyService.MyEvent), 
    OnMyEvent);
} 

是否可以避免取消订阅事件源或忽略弱事件模式,取决于事件源的生命周期。

为了执行事件处理程序,事件源必须“知道”侦听器才能访问回调(或更技术地说,是为侦听器实例分配的内存空间)。因此,委托保留对实例的强引用,该引用存储在

Delegate.Target
属性中。

如果事件源

MyService
的寿命比侦听器
ViewModel
长,则侦听器无法被垃圾回收,直到事件源本身被垃圾回收或强引用被删除(例如通过取消订阅或设置事件委托)到
null
)。

这种情况是可能的,例如,当事件源是聚合实例时,允许在类范围之外存在或引用,例如通过公共属性或作为方法的返回值或定义事件源

static 
.

在您的代码中

MyService
(事件源)已定义
public
。这意味着
ViewModel
(事件监听器)无法控制此实例的生命周期。
如果
ViewModel
范围之外的某个生命周期比
ViewModel
更长的实例获得对此
public
属性值的引用,则
MyService
(因此事件侦听器
ViewModel
)将保持活动状态,即使if
ViewModel
将属性
MyService
设置为
null

如果属性

MyService
private
并且您永远不会将此属性的引用返回给
public
方法的调用者,那么您应该是安全的,因为
MyService
的生命周期现在与
ViewModel
的生命周期。破坏
ViewModel
也会破坏
MyService

换句话说,您必须保证事件源的生命周期与事件侦听器的生命周期耦合(或更短),或者它们之间没有耦合(弱事件模式,取消订阅)。

您最好始终遵循订阅/取消订阅或

WeakEventManager
的模式。这样你就不必担心对象的生命周期来防止内存泄漏。

如何实现弱事件模式


0
投票

否则,您可以使用Lambda Function来管理事件,它会自动销毁。

© www.soinside.com 2019 - 2024. All rights reserved.