Sunday, March 22, 2009

Using extension methods

How many times you have seen your project having a StringUtils or a similar Helper class ? Most of the helper and util classes has pure procedural code. e.g StringUtils may contain a method IsNumaricString() or ToTitleCase( ) which takes a string as parameter.
Ideally all these methods should go in String class itself but we cann't extend String class as it is sealed. However, with the extension methods in C# 3.0, we can 'open' the classes and add methods to it.

So, instead of
string name = StringUtil.ToTitleCase("foo bar");
we can say -
"foo bar".ToTitleCase()

The extension method is defined as -

public static class StringX {
public static string ToTitleCase(this string str)
{
return string.IsNullOrEmpty(str) ? string.Empty : Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(str.ToLower());
}
}

The extension method is defined as a static method, in a static class. The first argument to the method is the type which we went to extend, preceded with 'this' modifier. When we call the extension method, we don't need to specify this first argument.
Isn't it neat ? The example I have used is the simplest one. There are many other cases when you can extend the functionality of the .Net system classes. Like, we can add Serialize() as the extension method in the Object class.

When I first came across this feature, I thought its the way to open classes in Ruby style. But well, its not exactly the same.
In extension method, we cann't access the private (or protected) members of the class that the method extends. We cann't use extension methods to override a method in the class.
The method defined in the actual type has precedence over the extension methods.So there is no way you can change the behavior of existing method with extension method.

One more good thing with extension methods is that they are available only on classes which refers to them. We need to import the classes that contains the extension methods.

The compiler translates the extension method call to the call on static method. So, its just a syntactic sugar to make your code look better. You cann't do 'effective' monkey patching with it as you cann't shadow a method defined in the actual type, neither you can access (and change the value of) any private member of the object.

However, we shouldn't use the extension methods haphazardly for extending the class behavior. Its definitely not the alternative for subclassing.

The MSDN says to use extension methods seriously and only when we have to. Well, my take is to use extension methods when its appropriate.
We can use it when its not possible or not appropriate to extend the class .e.g if we are using some class from the third party code.
Another best place to use extension methods is when you want to add a functionality to your data contract. Consider a scenario where you are getting a Booking object from some service. You want to check if the booking is valid based on some properties of the object. So your code will be something like this...

if(booking.BookingDate > DateTime.Now && booking.NumberofPassengers > 0 && booking.BookingCost > 1)
// then booking is valid

Here, we are accessing properties from the booking object to decide if the booking is valid. This is clear violation of "Tell, Don't Ask" principle (http://c2.com/cgi/wiki?TellDontAsk). The
best place for this logic is Booking class itself. But Booking is a data contract and we cann't add methods to it.So we can add an extension method IsValid() in a static class called
BookingX.

public static bool IsValid(this Booking booking){
return booking.BookingDate > DateTime.Now && booking.NumberofPassengers > 0 && booking.BookingCost > 1;
}

Now, by importing the BookingX class, instead of all the above code, we can say -
booking.IsValid()

So, extension methods helps you to keep the behavior in the correct class.

No comments: