我是否需要担心 Rx.NET FromEventPattern 的内存泄漏?

问题描述

在 .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 )调用。这对对话框等工作正常。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...