Use extension methods judiciously

Extension methods are an interesting bit of compiler trickery that lets you “add” methods to classes that are sealed or to classes and interfaces for which you don’t have the source. From the programmer’s perspective, creating an extension method is “just like” adding an instance method.

For example, suppose you have a method that checks a string to see if it’s a palindrome:

public static class StringTools
    public static bool IsPalindrome(string s)
        int i = 0;
        int j = s.Length-1;
        while (i < j)
            if (s[i] != s[j])
                return false;
        return true;

You can then determine if any string is a palindrome by passing the string to the IsPalindrome method:

string foo = Console.ReadLine();
bool pal = StringTools.IsPalindrome(foo);

And then you find yourself in a fit of object-oriented pique thinking, “but IsPalindrome should be a method of the String class!” And you rant for an hour about inconsiderate runtime library designers who make your life more difficult by sealing classes and preventing you from extending things as you’d like.

And then you discover extension methods, and your life is changed. With but a minor change, you have an instance method! All you have to do is change the method declaration:

    public static bool IsPalindrome(this string s)

And now you can write:

string foo = Console.ReadLine();
bool pal = foo.IsPalindrome();

Reveling in your newfound power, you go through your entire code base, converting every utility function to an extension method. You end up with all kinds of interesting things. For example, why call TimeSpan.FromSeconds(10) when you can easily create an extension method on the int type, giving you Timespan bar = 10.Seconds();? Yay!

Please, for the sake of the poor programmer who will follow you (including yourself, six months from now), do not give in to this temptation.

An extension method is nothing but a bit of compiler trickery. The addition of the this modifier in the parameter declaration just tells the compiler that it can perform a simple substitution. When the compiler sees foo.IsPalindrome(), it looks in the string class to see if there is an IsPalindrome method. If there isn’t, the compiler then checks its symbol table to see if there is an extension method somewhere called IsPalindrome that takes a single String parameter. If there is, then the compiler generates code to call it. The generated code will be exactly what you wrote before: StringTools.IsPalindrome(foo).

The only thing you gain from using extension methods in the way I described above is a little bit of expressiveness. That’s all. And it comes at a great cost. Using extension methods makes your code easier to read, but more difficult to understand. When somebody who’s unfamiliar with your code sees foo.IsPalindrome(), the first reaction will be something like, “WTF? I thought foo was a String. There’s no String.IsPalindrome method!.”

It gets worse if you create silly things like 10.Seconds() to turn a number into a TimeSpan. Seeing that, I would expect there to be extension methods for hours, minutes, milliseconds, and days. And I would expect there to be similar extension methods for the other numeric types. After all, if I want twelve and a half seconds, I shouldn’t have to write 12500.Millseconds(). I should be able to write 12.5.Seconds(). But, really, why duplicate effort? There already exist conversion functions: TimeSpan.FromSeconds, etc. Better to use them.

I’ve seen all too many code bases filled with extension methods that are there only because some programmer thought it was a cool thing to turn his incomplete utility function library into a hodgepodge of extension methods. When asked, the culprit has no good reason for doing it and if pressed, most will admit that it was a mistake.

The C# team added extension methods primarily to support LINQ–to provide a default implementation for an interface method. When used for that purpose, extension methods are appropriate. I find it unfortunate that the language team didn’t limit their use to that particular case, because all other uses of extension methods, in my opinion, cause more problems than they solve.