Sunday, 5 August 2012

C#: Extension methods

Extension methods were introduced into .NET 3.5 and allows you to augment functionality to a class without changing the class. Sounds not quite right though. What you actually do is write functionality that operates on an instance of a class using the public methods/fields/properties of that class and looks like you have added functionality.

We define an extension method as a static method within a static class. The first parameter of the method identifies which class you are augmenting which is prefixed with the this keyword.

Let's create a simple extension method which operates on the long type. This method will return a bool if the number is PanDigital. A PanDigital is a number which amongst it's significant digits the number is used at least once, for example the numbers 1234567890 and 13402456789 are pandigital.

The signature of this method (within out static class) will be
public static bool IsPanDigital(this long number)
The main difference between this method and normal methods is the use of the this keyword before the type of the first parameter. Once this method is written the method could be used as
long number = 1234567890;
bool pd = number.IsPanDigital();
The full method could look like this
public static class PanDigitalExtentionMethod
{
    public static bool IsPanDigital(this long number)
    {
        string strNumber = Convert.ToString(number);

        for (int i = 0; i < 10; i++)
        {
            if (!strNumber.Contains(i.ToString()))
                return false;
        }
        return true;
    }
}
When using this you add any correct references and using statement or put all your extension methods in one namespace (normally in one directory).

The method you write looks as if there is no difference between calling the extension method and any other method that is defined in the class.

Where is this used?

I first (explicitly) came across this when developing a MVC (web) application. Common practice is to create "Html Helpers", so for example with the following extension method
namespace Helpers
{
  public static class HtmlHelpers
  {
    public static string OrdinalSuffix(this HtmlHelper helper, int input)
    {
      if (input == 0)
          return "";
      else
      {
          //can handle negative numbers (-1st, -12th, -21st)
          int last2Digits = Math.Abs(input % 100);
          int lastDigit = last2Digits % 10;

          //the only nonconforming set is numbers ending in 
          //   <...eleventh, ...twelfth, ...thirteenth> 
          return input.ToString() + 
              "thstndrd".Substring((last2Digits > 10 && last2Digits < 14) 
                                            || lastDigit > 3 ? 0 : lastDigit * 2, 2);
       }
    }
  }
}
This method will take an integer (e.g. 2) and return a string with the number followed by it's ordinal suffix (e.g. 2nd). You could then use this from within a View. For example, within Razor you could have code such as
<p>
<%: Html.OrdinalSuffix(history.Position) %>
</p>
In a MVC application you would normally edit the web.config file to add the namespace for the Helpers (under the system.web/pages/namespaces node) - thus making it available in all your pages without you having to add an explicit using clause.

Now, I said MVC was the first time I encountered extension methods - in fact, I said the first time I explicitly encountered them. The .NET development team just didn't decide to add this functionality. Whilst it looks cool to use - I have reservations about using extension methods as we have used them above. If I were looking at some code and I saw a method .IsPanDigital then I would need to make the jump that this was an extension method. Although, if you put the mouse over the call (in Visual Studio) it will tell you that it is an extension method.

So why were extension methods added - to help support LINQ. You may well have used them without knowing about it. Let's take an example where we want to retrieve from a List the strings that are three characters in length. We will work with this list
List<string> inputs = new List<string> { "one", "two", "three", "four", "five" };
Code to do this with LINQ might be
var threeLinq = from r in inputs
                where r.Length == 3
                select r;
foreach (var result in threeLinq)
    Console.WriteLine(result);
Running this will give the outputs "one" and "two". But underneath this code is what is being run
var three = inputs.Where(s => s.Length == 3);
foreach (var result in three)
    Console.WriteLine(result);
The function Where is an extension method (of string). Putting our mouse over the Where will show you.

Another quick example
var ordered = inputs.OrderByDescending(i => i);
foreach (var result in ordered)
    Console.WriteLine(result);

var orderedLinq = from r in inputs
                  orderby r descending
                  select r;
foreach (var result in orderedLinq)
    Console.WriteLine(result);
This example shows using the OrderByDescending extension method - in Linq you use the orderby and descending keywords.

In both the LINQ examples we are using the select keyword. Let's look at these - we will retrieve our output in upper case
var three = inputs.Where(s => s.Length == 3).Select(s => s.ToUpper());
foreach (var result in three)
    Console.WriteLine(result);

Console.WriteLine("---");
var threeLinq = from r in inputs
                where r.Length == 3
                select r.ToUpper();
foreach (var result in threeLinq)
    Console.WriteLine(result);

In LINQ using select r.ToUpper() translates into using the Select extension method.

No comments:

Post a Comment