views:

1585

answers:

3

I'm hoping to clear some things up with anonymous delegates and lambda expressions being used to create a method for event handlers in C#; for myself at least.

Suppose we have an event that adds either an anonymous delegate or lambda expression [for you lucky crowds that can use newer versions of .NET].

SomeClass.SomeEvent += delegate(object o, EventArg e) { //do something };

I have read that people in the past have forgotten about events that still have handlers which prevent the class from being garbage collected. How would one go about removing the added handler without just setting SomeEvent to null within the class. Wouldn't the following be an entirely new handler?

SomeClass.SomeEvent -= delegate(object o, EventArg e) { //do same thing };

I could see storing the anonymous delegate / lambda expression in a variable, but that, to me at least, seems to defeat the entire purpose of being able to simply and succinctly add an event handler.

SomeEventDelegate handler = new SomeEventDelegate(delegate(object o, EventArg e) { //do same thing });
SomeClass.SomeEvent += handler;
//... stuff
SomeClass.SomeEvent -= handler;

Again, I understand that you could just do...

public override Dispose(bool disposing)
{
    _someEvent = null;
    this.Dispose();
}

But I'm more interesting with just removing the dynamically created method from the Event. Hopefully someone can shed some light onto this for me. Thanks!

A: 

I think the problem is you seem to be proceeding from the assumption that having a delegate assigned to an object's event prevents it from being GCed.

This as a simple statement is not true.

With that said the perceived problem disappears.

Initially in garbage collection everything is garbage. The GC runs through every thing currently available globally and on each stack and from these those other objects that they are referencing and so on, marking each as not garbage.

How would such a graphing process manage to arrive at this object?

AnthonyWJones
The statement is true, since an event handler is added to an internally managed list of handlers.
Kent Boogaart
Closures can bite you in the ass if you're not careful.
Will
@Will: You really need to explain yourself. Perhaps with your own answer and an example of how a closure would bite one so?
AnthonyWJones
@Kent: Do you have reference regarding this internally managed list of handlers?
AnthonyWJones
Look here for a bite of the ass: http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling
Benjol
+5  A: 

If object X has an event handler whose target is object Y, then object X being alive means that object Y can't be garbage collected. It doesn't stop object X from being garbage collected.

Normally when something is disposed, it will become garbage pretty soon anyway, which means you don't have a problem.

The problem with events and GC is if you forget to remove a subscribed handler from a different object - i.e. you have a listener which is disposed, but will never be garbage collected because there's still a reference to it from the event in a different object.

Jon Skeet
One of the pitfalls of delegate event handlers is that, while they appear to be disconnected to the classes in which they are defined, you might have a compiler-generated closure that keeps the defining class instances from being collected.
Will
Fun fact: when a ToolStrip becomes visible, it registers with System.UserPreferenceChanged. So if you remove a ToolStrip from its container without setting Visible to false, it never gets disposed. Things like this are why sooner or later you need a memory profiler.
Robert Rossney
+1  A: 

You can't.

Just like you can't create anonymous type outside of its scope (except for some compiler tricks).

That's why it's called anonymous.

You have to save a reference somewhere... or use reflection.

chakrit
I would imagine it to be the same for anonymous objects. SomeClass.SomeEvent += new SomeEventHandler(SomeMethod); wouldn't be able to be removed without setting the event to null, no?
Nicholas Mancuso