Click to See Complete Forum and Search --> : events + anonymous method question


Grofit
November 18th, 2009, 11:44 AM
Hey,

Normally when you are using events and you add a delegate you can easily unsubscribe when cleaning up the object (on a side question if you dont will it not be GCed until it is?).


// Subscribe
SomeObject.SomeEvent += SomeObject_SomeEvent;

void SomeObject_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine("Called Some Event");

// Unsubscribe (could cast sender)
SomeObject.SomeEvent -= SomeObject_SomeEvent;
}


However when you use anon methods (not sure if my syntax is 100% but you get the idea:


// Subscribe
SomeObject.SomeEvent += new delegate(object sender, EventArgs e)
{
Console.WriteLine("Called Some Event");
// How do i unsubscribe?
}



You cant seem to unsubscribe (or there is no simple way that stands out to me) so im wondering how you can remove the listener, as i basically want to have an objects event to be listened out for once then trigger something else and then stop listening...

BigEd781
November 18th, 2009, 11:47 AM
I don't believe that the spec guarantees anything about equivalence between anonymous methods, so if you need to unsubscribe you should just use the standard approach (example 1) or use a variable to hold the delegate:


var myDelegate = delegate(){Console.WriteLine("Called some event");};

Grofit
November 18th, 2009, 12:00 PM
Thats fine, but as i mentioned as a side question if you have outstanding subscribers to an objects events will it stop it from GCing? or does it just ignore them and tidy them up when it cleans up?

I cant see it stopping them as otherwise anon methods and events would cause all your objects to just linger in limbo, but i just want to confirm...

BigEd781
November 18th, 2009, 12:23 PM
Yes, delegates do count as a reference to the object. However, I think it is a moot point here; if you have delegates that need to be removed, don't use anonymous methods :). Now, in the case of an anonymous method I am not really sure of they are counted as a reference, but C# language specification would explain all of this.

Mutant_Fruit
November 18th, 2009, 05:35 PM
Anonymous delegates are converted at compile time into regular methods. However, two different anonymous methods with identical code inside them would convert to two distinct methods, therefore it's impossible to unsubscribe an anonymous delegate.

Also, as they're regular methods, they have exactly the same impact on object lifespan as a regular event handler would. It's also worth noting that if you reference any variable *inside* the anonymous delegate which is declared *outside* of the delegate, that variable will be kept alive until the anonymous delegate dies.

Grofit
November 19th, 2009, 05:02 AM
Wow... thats like memory waste HQ...

So anytime you use an anonymous delegate *anything* referenced within there and the subscriber will stay in memory until the application is closed down...

That sounds like a stupid thing to have in the language if it is so wasteful... is there no way to tell .net to clean up the anon method directly, which would in turn remove the reference on the objects? I guess its a back to front approach but surely they must have given you some way to clean up your mess...

Looks like im going to stick to fully declared methods for now then... thanks for the info...

Mutant_Fruit
November 19th, 2009, 10:41 AM
So anytime you use an anonymous delegate *anything* referenced within there and the subscriber will stay in memory until the application is closed down...

No, that's not what I said. I said if you attach an anonymous delegate to an event, any objects captured inside the scope of the anonymous delegate will be kept alive while the anonymous delegate is alive. The anonymous delegate is kept alive for as long as the object you attached it to is alive.

This is *exactly* the same semantics as attaching a regular event handler [0]. There is no difference whatsoever.

[0] The only (relatively minor) difference is that you can capture variables inside an anonymous method but for a regular event handler you'd have to create an additional class manually to 'capture' variables you want to persist.

Grofit
November 20th, 2009, 02:20 AM
Sorry i was just generalizing...

When you said that a subscribed anon method would still count as a reference to the object i was under the impression you would be in a catch 22, where you cant clear up the object because it has the anon method attached, and you cant clear up the anon method because it has the object attached, which lead me to think that if nothing could change either of those 2 conditions it would just sit there until the app closed and all memory used by the app was wiped clean... Which would in turn keep any variables used within the anon method alive until it could be cleared up...

Thats the thing which im unclear about, if this is not true then there must be a way to break the stalemate (for lack of a better word) where they are both reliant on the other closing first. As you say you cant lookup the anon method and unsubscribe, so is there a way to force the object to close regardless of those events, or at least just tell the object to clear its subscriber list... either of those options would cause everything to be released and cleared up right?

Mutant_Fruit
November 20th, 2009, 06:27 AM
An anonymous delegate doesn't explicitly keep the object it's attached to alive.



// A
public class Method ()
{
MyEvent += delegate {
Console.WriteLine ("Hello, i'm anonymous");
};
}


// B
public class Method2 ()
{
MyEvent += MyEventHandler;
}
void MyEventHandler (object o, MyEventArgs)
{
Console.WriteLine ("Hello, i'm not anonymous");
}

There is *zero* difference between the above two implementations. They are identical from both a performance and object lifetime point of view.


public void Method ()
{
MyOtherObject o = new MyOtherObject ();
MyEvent += delegate {
Console.WriteLine ("I'm keeping {0} alive", o);
};
}


In the above snippet 'o' is captured by the anonymous delegate. 'o' cannot die until the anonymous delegate dies. The anonymous delegate will die when the object that 'MyEvent' is declared on dies.


public void Method ()
{
MyEvent += delegate {
MyOtherObject o = new MyOtherObject ();
Console.WriteLine ("{0} is recreated every time the handler is called and dies when the handler exits", o);
};
}

Finally, in the above snippet, 'o' will be created and discarded every time the handler is invoked. 'o' is not captured by the delegate and so will not be kept alive for as long as the delegate is alive.

Does that clear things up? It can be a tricky concept to get your head around, but once you do it makes perfect sense.

Just as an appendum, this is more or less what the compiler does

This code:

public void Method ()
{
MyOtherObject o = new MyOtherObject ();
MyEvent += delegate {
Console.WriteLine ("I'm keeping {0} alive", o);
};
}


Is turned into this:


class GeneratedClass
{
MyOtherObject a;
void GeneratedHandler (object o, MyEventArgs e)
{
Console.WriteLine ("I'm keeping {0} alive", a);
}
}

public void Method ()
{
MyOtherObject o = new MyOtherObject ();
GeneratedClass c = new GeneratedClass ();
c.a = o;
MyEvent += c.GeneratedHandler;
}


That's the only 'magic' involved.

Grofit
November 20th, 2009, 06:52 AM
An anonymous delegate doesn't explicitly keep the object it's attached to alive.

THAT right there is what confused me as BigEd said it would keep a reference to the object, which i thought implied that it wouldnt be GCed (i mean the attached object)... So if they do not keep the objects alive like you say then everything makes perfect sense... it was just that earlier comment that gave me some doubt...