Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do I need to remove this sort of event handler?

Tags:

c#

.net

delegates

If I create a .NET class which subscribes to an event with an anonymous function like this:

void MyMethod()
{
   Application.Current.Deactivated += (s,e) => { ChangeAppearance(); };
}

Will this event handler be a root keeping my class from being garbage collected?

If not, wohoo! But if so, can you show me the removal syntax? Just using -= with the same code seems wrong.

like image 551
jschroedl Avatar asked Mar 04 '11 19:03

jschroedl


People also ask

Should I always remove event listeners?

If there is no memory leak, the used memory will increase by around 1000kb or less after the tests are run. However, if there is a memory leak, the memory will increase by about 16,000kb. Removing the event listener first always results in lower memory usage (no leaks).

Can event handlers be removed?

Most modern web browsers including Chrome, Firefox, and Edge support the removeEventListener() method.

Why do you need to remove event listeners?

If event listeners are not removed, it will cause memory leak for older browsers (IE 6 and 7, if i recall correctly). Hence will cause unnecessarily high memory utilization which will lead to many problems.

How do I delete an event handler?

Go to the objects tab, view the Document object (don't click on edit) and scroll down to Event Handlers. Select the one to delete and press delete.


Video Answer


4 Answers

You can either use a "real" method as Simon D. suggests, or do this:

EventHandler handler = null;
handler = (s,e) => { 
    Application.Current.Deactivated -= handler;
    ChangeAppearance();
};
Application.Current.Deactivated += handler;

Since this is somewhat ugly and defeats the purpose of using a lambda to be succint, I 'd probably go with refactoring it out to a method. But it's useful to know what else works.

Warning: It goes without saying that you have to be super ultra careful to not mess with the value of handler at all between the point where you subscribe to the event and the point where it is actually invoked, otherwise the unsubscribe part won't work correctly.

like image 100
Jon Avatar answered Oct 02 '22 15:10

Jon


I think you do need to unsubscribe, because the event provider (the application) lives - or at least can live - longer than your consumer. So each subscribing instance dying while the app remains living will create memory leaks.

You are subscribing an anonymous delegate to the event. This is why you can't unsubscribe it the same way, because you cannot address it anymore. Actually you are creating the method at the same moment you subscribe, and you don't store any pointer to the newly created method.

If you slightly change your implementation to use a "real" method, you can easily unsubscribe from the event the same way you subscribe to it:

Application.Current.Deactivated += ChangeAppearance;
Application.Current.Deactivated -= ChangeAppearance;
private void ChangeAppearance(object sender, EventArgs eventArgs)
{
    throw new NotImplementedException();
}
like image 28
Simon D. Avatar answered Oct 03 '22 15:10

Simon D.


You definitely have to clean up the reference. If there were any doubt you could easily test with your own static event like so.

static class MemoryLeak
{
    static List<Action<int>> list = new List<Action<int>>();
    public static event Action<int> ActivateLeak
    {
        add
        {
            list.Add(value);
        }
        remove
        {
            list.Remove(value);
        }
    }
}

Then by setting a breakpoint in the remove function you can see that your reference is not cleaned up.

class Program
{
    static void Main(string[] args)
    {
        foo f = new foo();
        MemoryLeak.ActivateLeak += o => f.bar();
        f.tryCleanup();
    }
}

class foo
{
    public void bar()
    { }

    public void tryCleanup()
    {
        MemoryLeak.ActivateLeak -= o => bar();
    }
}

As an alternative to Simon's solution you could use a second closure to create a "detach" action which can be passed around.

foo f = new foo();
Action<int> callfoo = o => f.bar();
MemoryLeak.ActivateLeak += callfoo;
Action cleanUp = () => MemoryLeak.ActivateLeak -= callfoo;

// Now you can pass around the cleanUp action and call it when you need to unsubscribe from the event.
cleanUp();
like image 22
Eric Avatar answered Oct 03 '22 15:10

Eric


This is indeed a leak that prevents garbage collection. There are ways around it - with WeakReference being one of the better ones.

This link has a good conversation and good answers for you.

like image 34
John Arlen Avatar answered Oct 01 '22 15:10

John Arlen