Should I unsubscribe from events?

C#EventsSubscribeUnsubscribe

C# Problem Overview


I have 3 questions concerning events:

  1. Should I always unsubscribe events that were subscribed?
  2. What happens if I do NOT?
  3. In the below examples, how would you unsubscribe from the subscribed events?

I have for example this code:

Ctor: Purpose: For database property updates

this.PropertyChanged += (o, e) =>
{
    switch (e.PropertyName)
    {
        case "FirstName": break;
        case "LastName": break;
    }
};

and this: Purpose: For GUI-binding wrap the model into viewmodels

ObservableCollection<Period> periods = _lpRepo.GetDailyLessonPlanner(data.DailyDate);
PeriodListViewModel = new ObservableCollection<PeriodViewModel>();

foreach (Period period in periods)
{
    PeriodViewModel periodViewModel = new PeriodViewModel(period,_lpRepo);
    foreach (DocumentListViewModel documentListViewModel in periodViewModel.DocumentViewModelList)
    {
        documentListViewModel.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument);
        documentListViewModel.AddDocumentDelegate += new Action(OnAddDocument);
        documentListViewModel.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument);
    }
    PeriodListViewModel.Add(periodViewModel);
}

C# Solutions


Solution 1 - C#

Well, let's take the last question first. You can't reliably unsubscribe from an event that you've subscribed to directly with a lambda expression. You either need to keep a variable around with the delegate in (so you can still use a lambda expression) or you need to use a method group conversion instead.

Now as for whether you actually need to unsubscribe, it depends on the relationship between the event producer and the event consumer. If the event producer should live for longer than the event consumer, you should unsubscribe - because otherwise the producer will have a reference to the consumer, keeping it alive for longer than it should be. The event handler will also keep getting called for as long as the producer produces it.

Now in many cases that's not a problem - for example, in a form the button that raises the Click event is likely to live for about as long as the form on which it's created, where the handler is typically subscribed... so there's no need to unsubscribe. This is very typical for a GUI.

Likewise if you create a WebClient solely for the purpose of a single asynchronous request, subscribe to the relevant event and start the asynchronous request, then the WebClient itself will be eligible for garbage collection when the request has finished (assuming you don't keep a reference elsewhere).

Basically, you should always consider the relationship between the producer and the consumer. If the producer is going to live longer than you want the consumer to, or it's going to continue raising the event after you're no longer interested in it, then you should unsubscribe.

Solution 2 - C#

  1. It depends. Usually it's a good idea, but there are typical cases where you don't need to. Basically, if you are sure that the subscribing object is going to outlive the event source, you ought to unsubscribe, otherwise this would create an unnecessary reference.

If however your object is subscribing to its own events, like in the following:

<Window Loaded="self_Loaded" ...>...</Window>

--then you don't have to.

  1. Subscribing to an event makes additional reference to the subscribing object. So if you don't unsubscribe, your object might be kept alive by this reference, making effectively a memory leak. By unsubscribing you are removing that reference. Note that in the case of self-subscription the problem doesn't arise.

  2. You can do like that:

    this.PropertyChanged += PropertyChangedHandler; ... this.PropertyChanged -= PropertyChangedHandler;

where

void PropertyChangedHandler(object o, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "FirstName": break;
        case "LastName": break;
    }
}

Solution 3 - C#

You don't have to desubscribe from an event when the instance that is subscribing has the same scope as the instance that is being subscribed to.

Lets say you are a form and you are subscribing to a control, these two together form a group. However, if you have a central class that manages forms and you have subscribed to the Closed event of that form, these do not form a group together and you must desubscribe once the form is closed.

Subscribing to an event makes the subscribed instance create a reference to the instance being subscribed to. This prevents garbage collection. So, when you have a central class that manages form instances, this will keep all forms in memory.

WPF is an exception because it has a weak event model where events are subscribed to using weak references and it will not hold the form in memory. However, it is still best practice to desubscribe when you are not part of the form.

Solution 4 - C#

You may take a look at this article on MSDN. Quote:

> To prevent your event handler from > being invoked when the event is > raised, simply unsubscribe from the > event. In order to prevent resource > leaks, it is important to unsubscribe > from events before you dispose of a > subscriber object. Until you > unsubscribe from an event, the > multicast delegate that underlies the > event in the publishing object has a > reference to the delegate that > encapsulates the subscriber's event > handler. As long as the publishing > object holds that reference, your > subscriber object will not be garbage > collected.

Solution 5 - C#

1.) Should I always desubscribe events that were subscribed?
Usually yes. The only exception is when the object on which you subscribed isn't referenced anymore and will be garbage collected soon.

2.) What happens if I do NOT?
The object on which you subscribed will hold a reference to the delegate, which in turn holds a reference to its this pointer, and thus you'll get a memory leak.
Or if the handler was a lamda it will hold onto whatever local variables it bound, which thus won't be collected either.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionElisabethView Question on Stackoverflow
Solution 1 - C#Jon SkeetView Answer on Stackoverflow
Solution 2 - C#VladView Answer on Stackoverflow
Solution 3 - C#Pieter van GinkelView Answer on Stackoverflow
Solution 4 - C#Darin DimitrovView Answer on Stackoverflow
Solution 5 - C#CodesInChaosView Answer on Stackoverflow