Design: Interface methods versus Extension methods
There’s a good thread on the ALT.NET mailing list about the use of extension methods over interface methods. Having used extension methods heavily in SyncLINQ, there’s something I wanted to highlight when it comes to choosing between regular methods and extension methods.
Combining extension methods with interfaces can be very powerful. The IEnumerable interface is a good example of this:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
When you implement IEnumerable (or, more commonly, the generic IEnumerable<T>), all you provide is a GetEnumerator method. However, thanks to the LINQ extension methods, there are a number of extensions that can be used over an IEnumerable:
public static class Enumerable
{
IEnumerable<T> Where<T>(this IEnumerable<T> source, ...);
IEnumerable<T> Select<T>(this IEnumerable<T> source, ...);
int Sum<T>(this IEnumerable<T> source, ...);
// Continues for about 120 other methods...
}
Now, imagine if IEnumerable looked like this, and every collection had to provide its own implementation:
public interface IEnumerable<T> : IEnumerable
{
IEnumerable Where(...);
IEnumerable Select(...);
int Sum(...);
// Continues for about 120 other methods...
}
It simply wouldn’t work. To give you an idea of how many classes implement IEnumerable<T> directly: there are over 100 classes in the .NET Framework source code alone. If each of them had to implement every LINQ method, it wouldn’t scale.
Moreover, when you release version 2.0 and you add a lot of new features to an interface, you break a lot of existing collections - suddenly we’re in the world of interface versioning. Extension methods are a good way of adding general purpose capabilities to an interface, without actually modifying the interface.
Compiler Resolution
This capability comes at a cost. Invoking an interface method is done at runtime, which gives you a lot of power to change the way the code will behave at runtime. Extension methods, on the other hand, are always resolved at compile time.
For example, suppose you had this class:
class Adder
{
public int Add(IEnumerable<int> itemsToAdd)
{
return itemsToAdd.Sum();
}
}
You could call it like this, using a regular collection of integers:
Adder.Add(new int[] { 78, 42, 3 });
Or you could call it like this, passing in a LINQ to SQL query:
Adder.Add(
new EmployeeDataContext().Employees.Select(e => e.YearsEmployeed)
);
While both of these work, in both scenarios, the Adder.Add method will use LINQ to Objects to do the sum. But, in the LINQ to SQL case, we may have wanted it to perform the Sum operation as part of the SQL query instead. Unfortunately there is no way to do this, since the compiler resolves the Sum() method at runtime based on the IEnumerable interface, rather than the IQueryable interface provided by LINQ to SQL.
If Sum() had been an instance method on an interface, instead of an extension method, the call could have been resolved at runtime based on the object passed into the Add() method. This also makes it hard to use dependency injection with interfaces that rely on extension methods for implementation, since the method calls are resolved at compile time rather than runtime.
Summary
This certainly isn’t a warning about extension methods or advice not to use them; in fact, you could argue that had Sum() been an interface method, they’d still be building LINQ and wouldn’t even have started LINQ to SQL. We just have to remember that there’s a difference in how they should be used.
My rule of thumb:
- If you are adding a capability to an interface, and you are sure compile-time resolution is all you need, extension methods may be appropriate. You should still exercise good design reasoning.
- If you are creating interfaces for use of inversion of control, or there is a possibility of creating different implementations of the methods that should be resolved at runtime, don’t use extension methods.
Above all, remember that extension methods are static methods. They should be used when you need general purpose operations over many implementations of the interface, rather than when you need specific implementations. Use them in cases where you would have used a static method, but want it to feel more natural - not to replace methods that should be on the interface.
Technorati Tags: c# 3.0,extension methods,alt.net
Filed under: C#

My thoughts almost exactly. At least some of us are considering the affects of these things. They are static methods, nothing more; the only difference with them is that they have a more object oriented look and feel.
Paul,
Although it seems to be sledom used in the Linq world, extension methods will result in a call to an instance method - if a matching instance method exists. So, in theory, if your target class needs special behaviour, it can get it by implementing a method that matches the signature of the extension method. I would assume, but I haven’t verified, that you could make that method virtual if you want. E.g. virtual method is favoured, at compile time, over the extension method.
Hi John,
The problem is the methods are *resolved* at compile time.
Suppose you had this:
void DoStuff(this IFoo foo);
class FooFactory
IFoo Create()
If your factory returns an object that provides its own instance implementation of DoStuff, it WILL NOT be invoked - the extension method will instead. This breaks many of the rules around polymorphism and good OO in general - and reminds us why extension methods are nothing more than static methods - with all their limitations.
Good point! I had not thought of the case where the compiler cannot “see” the instance method because it is not present in the compile-time type of the object. (I’m guessing the same will apply if the compile-time type is a base class, rather than an interface, and at runtime the variable contains a subclass which introduces its own implementation.)
Your comment sparked an idea, which I’ve blogged about here: http://dotnet.agilekiwi.com/blog/2008/01/resolving-extension-methods.html
compare va mortgage refinance rates
recklessness landlords?paten gazette?adaptors opaque