Click here to Skip to main content
15,891,136 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.5K   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 
(Namaste or Namaskaram or Vanakkum, your choice), Hi S. Sentil,

The link at the very top of the article to the "technical blog" is not a valid link.

The link to the first article takes you outside CodeProject.

Understanding what you are doing here requires you to read the first article; imho : this would be a better article if you just updated the first article, and put that up on Code Project.

I think if you clearly show what is the final, corrected, usable, code in complete form at the end of the article, that will be very helpful.

The techniques you are describing and showing are very interesting, and I look forward to giving a more complete article a high rating Smile | :)

best, Bill

"Many : not conversant with mathematical studies, imagine that because it [the Analytical Engine] is to give results in numerical notation, its processes must consequently be arithmetical, numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine numerical quantities as if they were letters or any other general symbols; and it fact it might bring out its results in algebraical notation, were provisions made accordingly." Ada, Countess Lovelace, 1844

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.