Anonymous Method Identity Crisis

Delegates, like strings, have done some work so that == behaves as expected, rather than as it should.  A delegate can be thought of as a tuple of the instance (or lack-of-instance in the case of delegates to static methods) and the method it invokes.  As long as those two items are the same, then the delegates are considered equal, regardless of whether or not there are 2 distinct GC objects holding that information.  This neat little trick allows people to subscribe to an event, and discard that delegate instance.  Then when they need to unsbsubscribe, they create a new delegate (to the same instance and method) and use that to unsubscribe.

Well that neat little coding pattern can burn you if applied to anonymous methods.

The easiest one to explain is the fact that the language spec does not require 2 anonymous methods with the same bodies to be implemented by the same method.  So if you have the same anonymous method block in two places the compiler might or might not make them into the same method (our compiler doesn't right now).

The second problem is a little trickier to explain.  Basically you have to remember that you don't directly control the instance of the delegate that points to an anonymous method.  It could be static, it could be the same as the current method, or it could be something else entirely.  If you really read closely my other posts, you can probably figure out how to read the code and determine what the compiler will do.   First that is just plain error prone.  It only takes one innocent edit to add or remove access to a parameter, a local, a field, or just about anything else, to change which instance the compiler will generate.  Secondly it is not spelled out int eh language spec, and is thus free to change between versions and implementations.

Now you might be tempted to, “so what if I can't unsubscribe from some event.”  Well the biggest problem is lifetime.  As long as the anonymous method is subscribed to that event, it could be keeping your class alive longer than needed.  That might then force you to use something like the allocation profiler to try and figure out why your memory characteristics suck.

So now that I've convinced everybody, here' the right way to do stuff.  If you ever need the delegate that points to a given anonymous method in more than one place (like to unsubscribe), keep your own copy.  Store the delegate in a local, a field, a static someplace before passing it off to anything else.  Do not rely on delegate equivalence, use good old fashioned reference equality.  Enjoy!


Comments (0)

Skip to main content