简体   繁体   中英

Do I have to worry about memory leaks with Rx.NET FromEventPattern?

In .NET I've been essentially raised to never ever forget to unsubscribe to events. In MVVM apps what I often end up with is this construct.

public class WindowVm
{
    public EntityModel MyModel { get; set; }

    void Subscribe()
    {
        MyModel.PropertyChanged += DoSomething;
    }

    void Unsubscribe()
    {
        MyModel.PropertyChanged -= DoSomething;
    }
}

I need to unsubscribe, because if I don't MyModel would keep a reference to the WindowVm and keep it alive for as long as MyModel lives.

I just found out about reactive extensions, but I can't find out whether I need to still think about this. I cant find anywhere that says so, but I also don't hear "oh and btw it solves that annoying event unsubscribe problem" which would be a killer argument.

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);
    }
}

I can't tell whether this would create an extra reference. And what about this way?

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));
    }
}

I have not worked with RX but the Subscribe method that you are using is returning an IDisposable which should then be used by the consumer to unsubscribe. Your method:

void Subscribe()
{    
    MyModel.PropChangedObservable.Subscribe(e => DoSomething(e.Sender, e.EventArgs));
}

Should then be:

IDisposable Subscribe()
{    
    return MyModel.PropChangedObservable.Subscribe(e => DoSomething(e.Sender, e.EventArgs));
}

Note: I'm guessing your code is not 100% complete as you are missing the method to be invoked on Subscribe and you have hard-coded DoSomething and I'm ignoring that part

So the observer of your model would first call subscribe and acquire the reference to IDisposable. Once the observer is finished then it should call Dispose on that reference.

To answer your question

I can't find out whether I need to still think about this

The answer is yes, you still need to think about this. However the RX does have automatic unsubscription but you need to know how many events you want to listen to. Check the answer of this SO question .

I am using this approach in WinForms and it will also work for WPF. Unfortunately, it changes the generated code, but VS won't replace it when you change things in designer.

public MainForm()
{
    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.

}

Open InitializeComponent method and in designer file change dispose method.

    '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

Implement OnDispose under the ctor (I usually place it before Load)

public void OnDisposing()
{
    var context = // TODO: get VM for current frm;
    if (context != null)
        context.Dispose(); // call dispose on VM.
}

Your VM should then unsubscribe in on Dispose method that will be called when UC is disposed (or later:D ). That will work OK for dialogs etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM