Click here to Skip to main content
15,883,978 members
Articles / Programming Languages / C#

Anonymous Methods as Event Handlers – Part 2

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
13 Sep 2009CPOL1 min read 19.4K   6   2
Anonymous methods as event handlers

The previous post discussed having anonymous methods as event handlers and ended with a question – why doesn’t unsubscription work while subscription works out alright?

Vivek got the answer spot on – the way the C# compiler handles and translates anonymous methods is the reason.

Here’s the code involved:

C#
 1: public void Initialize()
 2: {
 3:     control.KeyPressed += IfEnabledThenDo(control_KeyPressed);
 4:     control.MouseMoved += IfEnabledThenDo(control_MouseMoved);
 5: }
 6:
 7: public void Destroy()
 8: {
 9:     control.KeyPressed -= IfEnabledThenDo(control_KeyPressed);
10:     control.MouseMoved -= IfEnabledThenDo(control_MouseMoved);
11: }
12:
13: public EventHandler<Control.ControlEventArgs>
           IfEnabledThenDo(EventHandler<Control.ControlEventArgs> actualAction)
14: {
15:     return (sender, args) => { if (args.Control.Enabled) actualAction(sender, args); };
16: }

The compiler translates IfEnabledThenDo into this:

C#
1: public EventHandler<Control.ControlEventArgs>
          IfEnabledThenDo(EventHandler<Control.ControlEventArgs> actualAction)
2: {
3:     <>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
4:     CS$<>8__locals2.actualAction = actualAction;
5:     return new EventHandler<Control.ControlEventArgs>(
                      CS$<>8__locals2.<IfEnabledThenDo>b__0);
6: }

Now the problem should be fairly obvious – every time the function is called, a new object gets created, and the event handler returned actually refers to a method (<IfEnabledThenDo>b__0) on the new instance. And that’s what breaks unsubscription. –= will not remove a delegate of a different instance of the same class from the invocation list – if it did, the consequences would not be pleasant if multiple instances of the same class subscribe to an event.

But why does the compiler translate our lambda expression this way? Raymond Chen has a great blog post explaining why, but the short answer is that it is needed to “hold” the actualAction (the method parameter to IfEnabledThenDo) so that it is available when the event handler actually executes.

Now that we know why, the way to get around this issue is to cache the delegate instance returned by IfEnabledThenDo and use the same instance for subscription and unsubscription.

C#
 1: EventHandler<Control.ControlEventArgs> keyPressed;
 2: EventHandler<Control.ControlEventArgs> mouseMoved;
 3:
 4: public void Initialize()
 5: {
 6:    keyPressed = IfEnabledThenDo(control_KeyPressed);
 7:    mouseMoved = IfEnabledThenDo(control_MouseMoved);
 8:
 9:    control.KeyPressed += keyPressed;
10:    control.MouseMoved += mouseMoved;
11: }
12:
13: public void Destroy()
14: {
15:    control.KeyPressed -= keyPressed;
16:    control.MouseMoved -= mouseMoved;
17: }

Knowing how things work under the hood has its advantages, I guess. :)

P.S.: A very small syntactic change to the original example would have made the code work right away. If you’ve followed along this far, you should be able to figure out why.

C#
 1: public void Initialize()
 2: {
 3:    actualAction = control_KeyPressed;
 4:    control.KeyPressed += IfEnabledThenDo();
 5: }
 6:
 7: public void Destroy()
 8: {
 9:     control.KeyPressed -= IfEnabledThenDo();
10: }
11:
12: EventHandler<Control.ControlEventArgs> actualAction;
13: public EventHandler<Control.ControlEventArgs> IfEnabledThenDo()
14: {
15:     return (sender, args) => { if (args.Control.Enabled) actualAction(sender, args); };
16: }

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Atmel R&D India Pvt. Ltd.
India India
I'm a 27 yrs old developer working with Atmel R&D India Pvt. Ltd., Chennai. I'm currently working in C# and C++, but I've done some Java programming as well. I was a Microsoft MVP in Visual C# from 2007 to 2009.

You can read My Blog here. I've also done some open source software - please visit my website to know more.

Comments and Discussions

 
Generallink broken, links refer to sources outside CddeProject, etc. Pin
BillWoodruff13-Sep-09 21:04
professionalBillWoodruff13-Sep-09 21:04 
GeneralRe: link broken, links refer to sources outside CddeProject, etc. Pin
S. Senthil Kumar13-Sep-09 21:31
S. Senthil Kumar13-Sep-09 21:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.