Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do i have to unsubscribe from anonymous event handlers of local variables?

If I have a code that looks something like this:

public void Foo()
{
    Bar bar = new Bar();

    bar.SomeEvent += (sender, e) =>
    {
        //Do something here
    };

    bar.DoSomeOtherThingAndRaiseSomeEvent();
}

Will bar be collected when the method runs out of the scope, or will I have to manually unsubscribe from the event to prevent a memory leak because of a reference to SomeEvent?

like image 209
Flagbug Avatar asked Feb 22 '11 17:02

Flagbug


People also ask

Do you need to unsubscribe from events?

In order to prevent resource leaks, you should 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.

Why do we use anonymous event handlers in Windows Forms?

Anonymous methods are a simplified way for you to assign handlers to events. They take less effort than delegates and are closer to the event they are associated with. You have the choice of either declaring the anonymous method with no parameters or you can declare the parameters if you need them.

Which operator would you use to have an event handler method subscribe to an event?

+= subscribes to an event. The delegate or method on the right-hand side of the += will be added to an internal list that the event keeps track of, and when the owning class fires that event, all the delegates in the list will be called.

When would you use an event handler?

Use the EventHandler delegate for all events that don't include event data. Use the EventHandler<TEventArgs> delegate for events that include data about the event. These delegates have no return type value and take two parameters (an object for the source of the event and an object for event data).


3 Answers

Your situation is fine; the event subscriber will not prevent the publisher from being collected, but the opposite can happen.

For example,

class Foo
{
    public event EventHandler FooEvent;

    void LeakMemory()
    {
        Bar bar = new Bar();

        bar.AttachEvent(this);
    }
}

class Bar
{
    void AttachEvent(Foo foo)
    {
        foo.FooEvent += (sender, e) => { };
    }
}

In this case, the instance of Bar created within LeakMemory can't be collected until either

  • The anonymous method represented by the lambda is removed from FooEvent's invocation list
  • The instance of Foo to which it's attached can be collected

This is because the event (which is just some syntactic sugar over an ordinary delegate instance) holds onto a list of delegates to invoke when it's invoked, and each of these delegates has, in turn, a reference to the object that it's attached to (in this case, the instance of Bar).

Note that we're only talking about collection eligibility here. Just because it's eligible doesn't say anything about when (or even, really, if) it will be collected, just that it can be.

like image 193
Adam Robinson Avatar answered Oct 22 '22 13:10

Adam Robinson


Well, the object bar refers won't be automatically garbage collected immediately... it's just that the bar variable won't prevent it from being garbage collected.

The event handler won't prevent the instance of Bar from being garbage collected either though - the "normal" problem is that an event handler keeps the subscriber of an event from being garbage collected (if it uses an instance method or captures "this" in an anonymous function). It doesn't usually affect the publisher being garbage collected. Just remember that the publisher needs to keep a reference to all the subscribers - the subscriber doesn't need to remember what it's subscribed to, unless it explicitly wants to unsubscribe or use some other member later.

Assuming nothing else is keeping your instance of Bar alive, your code should be fine.

like image 30
Jon Skeet Avatar answered Oct 22 '22 14:10

Jon Skeet


The above answers are correct; I just wanted to make a note. Anonymous delegates used as handlers can only be unsubscribed if you retain some other reference to the delegate/lambda. This is because lambdas are "function literals", kind of like string literals, however unlike strings they are NOT compared semantically when determining equality:

public event EventHandler MyEvent;

...

//adds a reference to this named method in the context of the current instance
MyEvent += Foo;

//Adds a reference to this anonymous function literal to MyEvent
MyEvent += (s,e) => Bar();

...

//The named method of the current instance will be the same reference
//as the named method.
MyEvent -= Foo;

//HOWEVER, even though this lambda is semantically equal to the anonymous handler, 
//it is a different function literal and therefore a different reference,
//which will not match the anonymous handler.
MyEvent -= (s,e) => Bar();

var hasNoHandlers = MyEvent == null; //false

//To successfully unsubscribe a lambda, you have to keep a reference handy:

EventHandler myHandler = (s,e) => Bar();

MyEvent += myHandler;

...

//the variable holds the same reference we added to the event earlier,
//so THIS call will remove the handler.
MyEvent -= myHandler;
like image 29
KeithS Avatar answered Oct 22 '22 13:10

KeithS