Tuesday, August 5, 2008

C# 3.0 Extension Methods

I'm getting stuck into C# 3.0 and really enjoying some of the new features. How often do you go looking for a class method to perform a specific task only to discover the method doesn't exist or exists but only does part of the job. Inevitably my programs contain methods I've created specifically to address this issue. One of the downsides of this approach is that there's often no obvious place to code the method, it just gets stuck close to where it is used or in a utility class if it is used in more than one location. And that is bad because it increases coupling/lowers cohesion for classes. Extension methods are a great way to address this situation.


I have an ASP.Net application that began life as a real application. The version I use to explore new stuff now bears little resemblance to the original but when I wrote it I was surprised to find that System.Web.UI.Control.FindControl() doesn't recurse down through the child controls. As a result I wrote the following method in the code-behind for a MasterPage:



private static Control LocateControl( Control Ctrl, string Id)
{
   Control ctrlRet = Ctrl.FindControl( Id);
   if (ctrlRet == null)
   {
      for (int i = 0; i < Ctrl.Controls.Count && ctrlRet == null; i++)
      {
         ctrlRet = LocateControl( Ctrl.Controls[i], Id);
      }
   }
   return ctrlRet;
}


Using extension methods I can now instead code a new class ControlExtensions:



public static class ControlExtensions
{
    public static Control LocateControl(this Control Ctrl, string Id)
   {
      Control ctrlRet = Ctrl.FindControl(Id);
      if (null == ctrlRet)
      {
         foreach (Control childCtrl in Ctrl.Controls)
         {
            ctrlRet = childCtrl.LocateControl(Id);
            if (null != ctrlRet)
               break;
         }
      }
      return ctrlRet;
   }
}

Calling the original method is clunky —


Control ctrl = LocateControl( parentControl, controlId);

when compared to the new extension method —


Control ctrl = parentControl.LocateControl( controlId);

Very nice.

3 comments:

Josh said...

How do you feel about the fact that if you don't include the namespace that your Extension Method is located in your code won't compile, trying to call a method that doesn't exist on a builtin .Net Framework object?

I wonder if the tradeoff that extension methods bring is that when someone else comes along in maintanence mode later if they remove the wrong 'using' statement they'll have exactly this problem.

David Clarke said...

Good point and not something I had considered. With respect I think your example is somewhat contrived - I anticipate an experienced developer would be unlikely to remove a using statement without checking the implications and an inexperienced developer would be unlikely to remove a using statement.

I had anticipated I would be able to assemble a library of extension methods as opposed to the more traditional library of utility messages we all take with us from project to project.

Having used NetBeans for the last 8 months I got used to having the IDE take care of imports. ReSharper provides a similar option.

Josh said...

Sure, I think you're right about the improbability of that using statement being removed in maintanence mode and I guess that wasn't the best example. The project my team is working on at the moment is rather large and we have a few people working from time to time on each other's code. In our situation I've seen exactly this happen to more than one developer (by more than one I mean only two :)) while refactoring someone else's code.

I suspect that with time more people will encounter, and become familiar with, extension methods and this won't ever become much of a big deal. As you mentioned, the benefit's you get from having a library of extension methods rather than utility classes probably outweighs this minor downside.