Extension Methods

Thursday, October 9, 2008

Extension Methods are new to Visual Studio 2008 and they basically let you bolt on additional functionality to the side of an existing type or function.  You can see the offical definition here.  This functionality is especially useful with LINQ which I will show in a sample of shortly, but first let's look at a simple sample to see how you can use it.

Basic Sample

I am going to create a simple console application that takes a number and doubles it, tripples it, splits it, and divides it into thirds.  With one line of code I am going to call all of these functions.  Below is the class that contains the Extension Methods.

    6 namespace ExtensionMethodSample

    7 {

    8     public static class Extension

    9     {

   10

   11         public static decimal Double(this decimal amount)

   12         {

   13             return amount*2m;

   14         }

   15

   16         public static decimal Tripple(this decimal amount)

   17         {

   18             return amount*3m;

   19         }

   20

   21         public static decimal Split(this decimal amount)

   22         {

   23             return amount/2;

   24         }

   25

   26         public static decimal Thirds(this decimal amount)

   27         {

   28             return amount/3;

   29         }

   30     }

   31 }

Notice that for the method to be an extension, you have to define it as a static method, and you have to define the first parameter as "this" which will be the type value or method return value you are bolting this extension method on to.  You can have more parameters following but the first parameter is always the calling instance.

I can then call the methods above like this:

    8     class Program

    9     {

   10         static void Main(string[] args)

   11         {

   12             var amount = 5m;

   13

   14             var result = amount.Tripple().Split().Double().Thirds();

   15

   16             Console.Write(result);

   17             Console.Read();

   18

   19         }

   20     }

 The crazy calculation above actually return the original value.

 




Linq Sample

You are most likely going to be calling pre-defined Extensions rather than writing you own especially when using LINQ.  For example I can abreviate the followng LINQ statement: 

   10             var db = new NewsContextDataContext();

   11             return from n in db.NewsPosts

   12                    where n.IsPublished

   13                    select n;

 I can use extension methods and abbreviate it like this (thanks Resharper!):

   10             var db = new NewsContextDataContext();

   11             return db.NewsPosts.Where(n => n.IsPublished);

 The MVC Storfront Sample

On the ASP.net web site there is a bunch a screencasts and code samples mainly dedicated to implimenting a real world MVC web site, but one of the interesting approaches they have taken is that when they use LINQ to talk to the database, they actually have a methods that returns a basic query of data and then they have a set of Extension Methods that take the initial data set and filters it further depending on what is needed.  So for example if you look at my code in the sample of above, I can take that data set and return it as the type IQueryable<NewsPost>.  I can then take that type and if I want to Query it further, I can use an extension method to return a smaller data set.  Also, notice here I have more than one argument I am passing in.

   12         public static IQueryable<NewsPost> WithTopFrontPage(this IQueryable<NewsPost> posts, int take)

   13         {

   14             return (posts.Where(p => p.IsFrontPage)).Take(take);

   15         }

The above extension method takes the original data set and gets only the records that have the FrontPage indicator set to true, and then takes the top 5 records.  I then have a test that checks to see if I have indeed returned 5 records.

   70             var actual = respository.GetAllActive().WithTopFrontPage(5).ToList();

   71             var expected = 5;

   72             Assert.AreEqual(actual.Count, expected, "Expected result does not equal 5");

Yey it passed!  This is a pretty neat approach that should make retrieving data simpler.

Downside:

Now that I got us all excited about using this feature just a word of warning.  It seems to me that in using Extesnsion Methods we are violating the philosophy of "not talking to strangers."  In other words we are pretty tightly coupled here and if you do not have control over the object that you are bolting to your extension to, there could be problems down the road.  Actually even if you do have control, you still could be entering the "zone of pain."  So with that said, happy coding and use carefully.

comments powered by Disqus