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
?
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.
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.
+= 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.
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).
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
FooEvent
's invocation listThis 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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With