Tuesday, February 17, 2009

Extension methods series: use interfaces

In my previous post I talked about the basics of extension method. Obviously you can define an extension method on any type but be careful. Because extension method are public member of public class their scope is very wide. After a while you can end up so many added method to the base type that it would make the intellisense unusable.

One way to control the scope is to target the type or interface that will best suit the need. In fact it is usually better to use interface than concrete class whenever possible. Of course this will probably extend the number of types on which the extension apply but it will be based on its capabilities instead of its implementation. For example linq use this concept a lot by extending IEneumrable<T> interface. Take for example the “Where” extension method:

public static class Enumerable
{
	// ...
	public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
	{
		// ...
	}
	// ...
}
The power of this concept is the possibility to build a chain of simple method to resolve complex problem. For example look at this code:
var processes = System.Diagnostics.Process.GetProcesses();

var query = processes
    .Where(proc => proc.Modules.Count > 3)
    .OrderByDescending(proc => proc.VirtualMemorySize64)
    .Take(10)
    .Select(proc => proc);
Just by reading the code its easy to understand what it does. The order of instructions is important. It wouldn’t have the same meaning if lines 4 and and 5 were swapped.

All that to say that it is better to use interfaces as a source type for extension method. We can use extension method to transform a na interface into another one. For example the “ToLookup” extension method takes an “IEnumerable<TSource>” and convert it to an “ILookup<TKey, TSource>”. This transformation adds an indexer based on a key for any IEnumerable types.

var processes = Process.GetProcesses();

var idxProcesses = processes.ToLookup(proc => proc.Id);

using (var proc = idxProcesses[3119].SingleOrDefault())
{
    Console.WriteLine(proc.ProcessName);
}
For example in this sample I get the list of all running processes, promote the Id as the key and use it with an indexer to find the process number 3119.

As you can see extension methods provides endless possibilities if used carefully.

2 comments:

Anonymous said...

I think you've got the IEnumerable[T] example somwhat back to front. The extension methods aren't implemented on this interface to limit the scope; in fact precisely the opposite they are implemented here is because it is the *least specific* type, so as to give them the *widest possible scope* of anything that is enumerable!

Eric De C# said...

@greg: You are right. I updated the post. I was thinking of something else when I wrote that.