Friday, March 16, 2012

Extension methods in C#

I think these are clever. A neat compiler trick but where the idea came from I don't know.
What they provide.
They allow us to add methods to an object without changing the code for that object. Their main use is in LINQ.  How is it that LINQ can have a method that works on so many types. How did they do that? Well the solution was really quite clever - and simple.

At the bottom of LINQ is the IEnumerable interface and that is very basic and simple. It is something like this:

using System.Collections;

namespace System.Collections.Generic
{
public interface IEnumerable<out T> : IEnumerable
{
new IEnumerator<T> GetEnumerator();
}
}

GetEnumerator lets us enumerate, or loop through collections:

IEnumerable<string> names = new IEnumerable<string>[] {"Bill", "Will", "Jill" };
foreach (var name in names)
{
Console.WriteLine(name);
}

Really handy and used all the time.

As I said, IEnumerable is at the bottom of LINQ and Microsoft didn't want to change this every time a whizzo idea arrived so they created the Extension method. Using these, it appears as if IEnumerator has more and more methods, but actually it doesn't. It's smoke and mirrors. We can add methods to it, but not change it. What fun.

Using DateTime as an example
In C#, DateTime is a struct, and is sealed, so we can not inherit from it. If say, you wanted to add fancy functions to DateTime we would have a couple of options. We can write a utility class that you pass a DateTime object into and the date maths, the workings, can be hidden in there. We would therefore call this Utility class method throughout our code, passing the date object into it. We could, if we wanted to, make the class static with a static method. That's not really news.

In the example below there are two methods that show how we can implement a method on a DateTime. The first, GetPreviousYear(DateTime date) is the usual way to do this.
The second, GetPreviousYearExtensionVersion(this DateTime date), is the extension method way of doing things.
There are three important aspects to note about the extension menthol version:
  1. The class is static,
  2. The method is static,
  3. The parameter type declaration has "this" prefixing it.
namespace Stuff
{
class MyProg
{
public void DoMoreStuff()
{
DateTime date = new DateTime(2012, 3, 15);
int previousYear = MyDateHelper.GetPreviousYear(date);
DateTime date2 = new DateTime(2012, 3, 15);
int previousYear2 = date2.GetPreviousYearExtensionVersion();
}
}
public static class MyDateHelper
{
public static int GetPreviousYear(DateTime date)
{
return date.Year -1;
}
}
public static class MyDateHelperExtension
{
public static int GetPreviousYearExtensionVersion(this DateTime date)
{
return date.Year -1;
}
}
}

You can see that the first method is called using MyDateHelper.GetPreviousYear(date); 
That is ok, it seems reasonable because that's how we've done it for years. But the syntax of the extension method is much easier to understand I think. The previous call is, in this example, date.GetPreviousYearExtensionVersion(). That is a silly name and example. It would become, date.GetPreviousYear();
  • MyDateHelper.GetPreviousYear(date);
  • date.GetPreviousYear();
Now that is good.

No comments:

Post a Comment