In my previous post I have been talking about reducing code pollution by ‘private helper methods’. I think however that code pollution comes in different shapes and styles; covering the entire subject in a single post would be absolute madness.
In this article I will shed a light on my thoughts on anonymous event handlers, and when to use them. I will use an ASP.NET Web Forms scenario because it is a notorious generator of event handlers. It’s also a facilitator for piles of horrible, non-generic, non-reusable code if used by improperly educated hands, but that’s a different story. Anyways, don’t let the scenario scare you off if you haven’t touched Web Forms or ASP.NET whatsoever, because it’s still pure C# we’re talking here!
I am, however, referring to version 4.0 of the .NET Framework (and higher), and the (anonymous) delegates have gone through a significant maturity process. If you’re targetting versions 2.0 or higher of the .NET Framework you’re still capable of pulling the tricks off I’m about to demonstrate, but you will have to do some things manually that the cool EventHandler and Action classes from version 4.0 will do for you automatically. So, if version 4.0 or higher is not within your grasp, this excellent article might do you some good!
Right, let’s begin! Have you ever encountered code that resembles something like this?
protected void Page_Init(object sender, EventArgs e) { btnReload.Click += btnReload_Click; refresh(); } private void refresh() { grid1.DataBind(); grid2.DataBind(); } private void btnReload_Click(object sender, EventArgs e) { refresh(); }
Stop looking for errors; it just works. Also, it does its thing properly, right? But still, why pollute the class body with an event handler method if its sole purpose is nothing more but calling an already defined method? OK, it’s not the same as a ‘helper method’, and implementing event handlers as class members surely isn’t a bad thing. But, come on! This handler does nothing interesting! The meat here is located in the ‘refresh()’-method!
I think there is room for improvement here. So, what about this?
protected void Page_Init(object sender, EventArgs e) { refresh(); btnReload.Click += new EventHandler((object x_sender, EventArgs x_e) => { refresh(); }); } private void refresh() { grid1.DataBind(); grid2.DataBind(); }
That is technically the same implementation, right? Just with less code.
The EventHandler that is instantiated here is a declaration of a delegate, which of course is nothing but a reference to a method. The method here that is represented by the EventHandler is declared *inside* the delegate declaration; it does not have an identity such as a method name in the codebase. (It’s anonymous, get it?)
If you truly want to go crazy then you could also anonimize the ‘refresh()’-method. Like this:
protected void Page_Init(object sender, EventArgs e) { // Scope the anonymous method declaration so that they cannot be used elsewhere in this method. { Action refresh() = new Action(() => { grid1.DataBind(); grid2.DataBind(); }); refresh(); btnReload.Click += new EventHandler((object x_sender, EventArgs x_e) => { refresh(); }); } }
What I have done here is anonimizing the ‘refresh()’-method with the Action-class, which is also just a fancy wrapper for anonymous delegate implementations. Now, the class is void (pun intended!) of pollution by context-specific class members.
When you write blocks like this, always make sure that you scope the declaration. It’ll make sure that the ‘refresh()’-method can not be called anywhere else within the Page_Init event handler. Scoping also puts emphasis on ‘keeping it all together’, which is a nice-to-have upon initial code inspection.
So, when is this pattern justified? I usually go by these rules:
- In the Page_Init-method, both the ‘refresh’-method as any calling event handler implementations are exclusively declared, and not anywhere else in the class;
- The event handler does nothing but calling the ‘refresh’-method;
- The logic that is being executed is both too context-specific and trivial that implementing it in a dedicated class would not be worth the effort.
Also, don’t go anonimizing everything everywhere! Just do it where it feels right, such as in specific cases like this. If, for instance, the event handler did not exclusively call the ‘refresh()’-method but also mutates some data elsewhere then I’d already reconsider the anonimization we’ve done here.
Try to use it wisely and experiment with it, you’ll be glad you did!