Sunday 4 March 2012

How and when to use Extension Methods in C# and .NET


Extension Methods are just one of the many impressive features introduced with C# 3.0 (along with the Implicitly Typed Local Variables, Lambda Expressions, LINQ and other wonders of the .NET)
Sure, you will say, even kindergarten developers know that! But what exactly are those Extension Methods?
What kind of magic makes them possible and how / when (not) to use them?

Description: http://www.aspdotnetfaq.com/fckeditor/editor/images/smiley/msn/lightbulb.gif  In this article we will first briefly examine this new language feature, show some basics usages and then we will tackle some more advanced and concrete examples of Extension Methods used to provide natural DateTime operations (similar to Date and Time operations available in Ruby on Rails) so be sure to stay tuned!

First of all lets start looking at the roots of it and see what the almighty MSDN Documentation says on this subject:
Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.
For client code written in C# and Visual Basic, there is no apparent difference between calling an extension method and the methods that are actually defined in a type.
Well that sure sounds nice!
Until now we were kind of stuck with the existing built-in classes and types, and in order to extend them we had to inherit the new class from them and then add new functionality (if we were lucky enough that the original class is not marked as Sealed).
So with Extension Methods we get some new degrees of freedom.
We can add functionality to any existing class (for now only in the form of new static methods, but who knows, we might get some more options in the future, like Extension Properties and Extension Events etc).
Off course there are some limitations:
  • Extension Methods have to be implemented as static methods and in static classes (inside a non-nested, non-generic static class to be more precise).
  • You can use extension methods to extend a class or interface, but not to override them. An extension method with the same name and signature as an interface or class method will never be called. At compile time, extension methods always have lower priority than instance methods defined in the type itself.
  • Extension methods cannot access private variables in the type they are extending.
You can consider Extension Methods as a 'legal' way to add more static methods to existing classes without actually inheriting them.
But the funny thing is that unlike regular static methods of the class, you cannot call Extension Methods on a class level (you will get an compile time error if you try this), but instead you must invoke them on a instance of the class (as if they were some regular methods of the instance of that class, which they are not!!!).

Also, inside the Extension Method you can freely use public properties of the passed object instance on which the method is being invoked, you are by no means limited only to static object data. Only the Extension Method is static method, but the object on which is called is full, regular object instance.
Its little confusing but don't worry you will get used to it as soon as you start using it. Description: http://www.aspdotnetfaq.com/fckeditor/editor/images/smiley/msn/teeth_smile.gif

How this works under the hood? (again MSDN to the rescue):

"In your code you invoke the extension method with instance method syntax.
However, the intermediate language (IL) generated by the compiler translates your code into a call on the static method. Therefore, the principle of encapsulation is not really being violated.
"
Ok, lets start with some simple "Hello World" example (for those of you that want more advanced examples, be patient we will get to that soon, or just skip to the end of article):
Motivation: We will add a single Extension Method called Reverse() to the System.String class that will return the reversed copy of the string on which we will call this method.
First we must create a static class with the Reverse() static method.
Also we will define it inside a namespace so we can later import that Extension Method in our projects:

using System;

namespace StringExtensionMethods
{
    public static class StringHelpers
    {
        public static string Reverse (this string s)
        {
            char[] letters = s.ToCharArray();
            Array.Reverse(letters);
            return new string(letters);
        }
    }
}
If you look at Reverse() method signature you can notice the 'this' keyword that is mandatory when creating Extension Methods (this keyword actually tells the compiler that we are creating one).
Also you can see that 'this' keyword is followed by the actual type that we are extending (in this case its string type) and after this there is a name of the parameter (so we can reference it in the methods implementation) and we can choose the name as we wish, no especial limitations there.
Make note that the 'string s' parameter that the static method will receive will be the instance of the object on which we are calling the actual Extension Method.
IMPORTANT: Remember that when we will call the Reverse() method there is no need to pass that first parameter, runtime will do that 'automagically' for us. But more on that later.
So first lets see how to use this Reverse() method:
In our project we must include the namespace where we defined the Extension Method so that it become available.
using StringExtensionMethods;
And then in our code we can write:
string word = "alpha";
Console.WriteLine(word.Reverse());

and the text that will be printed in the console will be “ahpla”.
As we have already said, you see there is no need to pass the first parameters to our Extension Method because the instance of the object on which we are calling it is passed as the first parameter.
Off course, that does not mean we cannot pass any parameters!
We can add more parameters to the the method signature and then pass them when calling the extension method.
But the first parameter will always be the instance of the object we are using the method on.
We can optionally define (in Extension Method signature) and later (when calling) provide arguments for parameters 2 through n.


So lets quickly create an example with additional parameter. This time we will extend System.Int type and add to it MultiplyBy() method that will allow us to easily multiply any integer with another integer.
Here is the implementation:
 
namespace IntExtensionMethods
{
    public static class IntHelpers
    {
        public static int MultiplyBy(this int originalNumber, int numberToMultiplyWith)
        {
            return originalNumber * numberToMultiplyWith;
        }
    }
}
You see that we have added the second Int parameter to the method signature and we use it to multiply the original number with this second parameter called numberToMultiplyWith.

And here is how to use it:

First we must not forget to add the namespace to our using list:

using
IntExtensionMethods;

and then call our method on the instance of any Int with single parameter (as we said many times so far, the first parameter - the actual Int value on which we are calling the method - is passed by runtime so we dont have to do it):

int
resultShouldBeTwelve = 3.MultiplyBy(4);
Console.WriteLine(result.ToString());

Thats about it. Now we know how to use Extension Methods!
But wait, we are not finished yet! This is where all the fun begins.
Lets do some real magic. Imagine we have a project that will be heavily using DateTime object and making a lot of complex date and time calculations.
Even with all the nice functionality built-in into DateTime class still its sometimes tedious to do time calculations.
If you want to add two weeks to some DateTime instance here is how we would normally had to do it:
DateTime nextWeek = DateTime.Now + new TimeSpan(14, 0, 0, 0);

or the other way:

nextWeek =
DateTime.Now + TimeSpan.FromDays(14);

Looks a little messy, right?  Wouldn't it be more natural for us as humans to write our code something like this:
DateTime nextWeek = DateTime.Now + 2.Weeks();

or even nicer:
 
DateTime twoWeeksFromNow = 2.Weeks().FromNow();

Well, hold your breath, because with extension methods its very easy to accomplish this.
For example, here is the Extension Method for the System.Int32 class that returns TimeSpan of the length of the number of weeks depending on the value of the number (Int) on which you call it:

/// <summary>
/// Returns TimeSpan for given number of Weeks (number of weeks * 7)
/// </summary>
/// <param name="weeks"></param>
/// <returns></returns>
public static TimeSpan Weeks(this int weeks)
{
    return new TimeSpan(weeks * 7, 0, 0, 0, 0);
}

But this is just scratching the surface.
Here is some more functionality we could easily achieve combining that with more similar Extension Methods:

DateTime
tomorrow = DateTime.Now + 1.Days();
DateTime yesterday = DateTime.Now - 1.Days();           
DateTime changedHourTo14h = DateTime.Now.SetHour(14);
DateTime todayNoon = DateTime.Now.Noon();
DateTime tomorrowNoon = DateTime.Now.NextDay().Noon();
DateTime fiveDaysAgo = 5.Days().Ago();
DateTime twoDaysFromNow = 2.Days().FromNow();
DateTime nextYearSameDateAsTodayNoon = 1.Years().FromNow().Noon();

Now how about that ???  Description: http://www.aspdotnetfaq.com/fckeditor/editor/images/smiley/msn/teeth_smile.gif

If you got interested make sure you check out my CodePlex project Fluent DateTime that provides these and many more nice Extension Methods for handling DateTime.

If you have some ideas you can join the project and contribute to the .NET community.


This wraps up our journey to the Extension Methods.
Now before you get all excited and start writing them for each class in System namespace just one final note of warning:
Make sure that you don't use this powerful feature without self-control.
If used when not really necessary you are opening the Pandora's Box and you can make your code less readable, or you can find yourself writing Extension Methods to some class and instead what you should have done is to extend that class and add those methods to the descendant classes lower down the class hierarchy...
Here is what MSDN says on this subject:
Extension methods are less discoverable and more limited in functionality than instance methods. For those reasons, it is recommended that extension methods be used sparingly and only in situations where instance methods are not feasible or possible.
So use it, but wisely!

No comments :