I use WPF with MVVM. I have a ViewModel
that instantiates the object MyService
as a property. The ViewModel
subscribes to an event of MyService
. The MyService
property is bound to some Elements in the View
.
When the ViewModel
is not used anymore, will MyService
keep my ViewModel
alive and preventing the garbage collection (GC), because of the event subscription? If yes, is there an easy way to solve this? Where should I unsubscribe from MyService
? (I have no control over the one calling my View/Viewmodel though)
public class ViewModel
{
public MyService MyService { get; set; } = new MyService();
public ViewModel()
{
MyService.MyEvent += OnMyEvent;
}
private void OnMyEvent(object sender, EventArgs e)
{
// do something
}
}
In general, you should always unsubscribe from events, preferably in the event handler.
public void DownloadFile()
{
this.ServiceClient.DownloadCompleted += OnDownloadCompleted;
}
public void OnDownloadCompleted(object sender, EventArgs e)
{
this.ServiceClient.DownloadCompleted -= OnDownloadCompleted;
// Do something
}
In scenarios where you don't know the lifetime of the event source use the weak event pattern or alternatively the IDisposable
pattern (but the weak event pattern should be the preferred choice).
To implement the weak event pattern, you would try to use an existing WeakEventManager
implementation (e.g., PropertyChangedEventManager
). Or if non is present, you can use the generic WeakEventManager<T>
. Since this class uses reflection to resolve and subscribe to the event delegate, it is recommended to extend the abstract class WeakEventManager
to create a custom type.
See Microsoft Docs: Weak Event Patterns.
public MyService MyService { get; set; } = new MyService();
public ViewModel()
{
// MyService.MyEvent += OnMyEvent;
WeakEventManager<MyService, EventArgs>.AddHandler(
this.MyService,
nameof(MyService.MyEvent),
OnMyEvent);
}
If you can avoid to unsubscribe from the event source or to ignore the weak event pattern, depends on the lifetime of the event source.
In order to execute the event handler, the event source must "know" the listener in order access the callback (or more technically the memory space allocated for the listener instance). The delegate therefore keeps a strong reference to the instance, which is stored in the Delegate.Target
property.
If the event source MyService
lives longer than the listener ViewModel
, then the listener can't be garbage collected, until the event source itself is garbage collected or the strong reference is removed (e.g. by unsubscribing or setting the event delegate to null
).
This scenario is possible e.g., when the event source is an aggregated instance, that is allowed to live or be referenced outside the class' scope e.g., via a public property or as return value of a method or the event source is defined static
.
In your code MyService
(the event source) is defined public
. This means ViewModel
(the event listener) has no control over the lifetime of this instance.
If some instance outside the scope of ViewModel
with a longer lifetime than ViewModel
obtains a reference to the value of this public
property, MyService
(and therefore the event listener ViewModel
) will be kept alive, even if ViewModel
sets the property MyService
to null
.
If the property MyService
would be private
and you would never return a reference of this property to a caller of a public
method, then you should be safe, since the lifetime of MyService
is now coupled to the lifetime of ViewModel
. Destructing ViewModel
will also destruct MyService
.
In other words you must guarantee that the lifetime of the event source is either coupled to the lifetime of the event listener (or shorter) or that there is "no" coupling between them a all (weak event pattern, unsubscribe).
You better always follow the pattern of subscribe/unsubscribe or WeakEventManager
. This way you don't have to worry about object lifetime to prevent memory leaks.