问题描述
在 .NET 中,我基本上被提升到永远不会忘记取消订阅事件。在 MVVM 应用程序中,我经常以这种结构结束。
public class WindowVm
{
public EntityModel MyModel { get; set; }
void Subscribe()
{
MyModel.PropertyChanged += DoSomething;
}
void Unsubscribe()
{
MyModel.PropertyChanged -= DoSomething;
}
}
我需要取消订阅,因为如果我不订阅,MyModel 将保留对 WindowVm 的引用,并在 MyModel 存在期间保持它的活动状态。
我刚刚发现了反应式扩展,但我不知道我是否还需要考虑这个。我找不到任何这样说的地方,但我也没有听到“哦,顺便说一句,它解决了烦人的事件取消订阅问题”,这将是一个致命的论点。
public class WindowVm
{
public EntityModel MyModel { get; set; }
void Subscribe()
{
MyModel.PropChangedobservable.Subscribe(e => DoSomething(e.Sender,e.EventArgs));
}
}
public class EntityModel : ObservableBase
{
public IObservable<EventPattern<PropertyChangedEventArgs>> PropChangedobservable { get; private set; }
public EntityModel()
{
PropChangedobservable = Observable
.FromEventPattern<PropertyChangedEventHandler,PropertyChangedEventArgs>(
x => this.PropertyChanged += x,x => this.PropertyChanged -= x);
}
}
我不知道这是否会创建额外的参考。那么这种方式呢?
public class WindowVm
{
public EntityModel MyModel { get; set; }
void Subscribe()
{
Observable
.FromEventPattern<PropertyChangedEventHandler,PropertyChangedEventArgs>(
x => MyModel.PropertyChanged += x,x => MyModel.PropertyChanged -= x)
.Subscribe(e => DoSomething(e.Sender,e.EventArgs));
}
}
解决方法
我没有使用过 RX,但是您使用的 Subscribe 方法正在返回一个 IDisposable,然后消费者应该使用它来取消订阅。 你的方法:
void Subscribe()
{
MyModel.PropChangedObservable.Subscribe(e => DoSomething(e.Sender,e.EventArgs));
}
应该是:
IDisposable Subscribe()
{
return MyModel.PropChangedObservable.Subscribe(e => DoSomething(e.Sender,e.EventArgs));
}
注意:我猜您的代码不是 100% 完整的,因为您缺少要在 Subscribe
上调用的方法并且您已经硬编码了 DoSomething
而我忽略了那部分
因此,您模型的观察者将首先调用 subscribe 并获取对 IDisposable 的引用。观察者完成后,它应该在该引用上调用 Dispose
。
回答你的问题
我不知道我是否还需要考虑这个
答案是肯定的,你还是需要考虑一下。然而 RX 确实有自动取消订阅,但你需要知道你想听多少事件。检查this SO question的答案。
,我在 WinForms 中使用这种方法,它也适用于 WPF。 不幸的是,它更改了生成的代码,但是当您在设计器中更改内容时,VS 不会替换它。
public MainForm()
{
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
}
打开 InitializeComponent 方法并在设计器文件中更改处理方法。
'UserControl overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
OnDisposing() // <-- Add this line
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub
在ctor下实现OnDispose(我一般放在Load之前)
public void OnDisposing()
{
var context = // TODO: get VM for current frm;
if (context != null)
context.Dispose(); // call dispose on VM.
}
然后,您的 VM 应该取消订阅 Dispose 方法,该方法将在 UC 释放时(或稍后 :D )调用。这对对话框等工作正常。