简体   繁体   中英

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 ?

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.

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.

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;

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