One of the things one can do with extensions methods is add implementation to an interface. It is ironic this is oftentimes overlooked, since the primary purpose of extension methods is to extend IEnumerable<T> for LINQ. Here is a simple example of how you can to take advantage of this.
I have a method that accepts an IEnumerable<Client> as a parameter. It will save the names of these clients to text file in the same directory as the program called clients.txt.
void SaveClients(IEnumerable<Client> clients, string fileName)
{
File.WriteAllLines(fileName, clients.Select(c => c.FullName));
}
File.WriteAllLines will write our file out, but we need to pass in an IEnumerable<string> for this to work in .NET 4 (use .ToArray() in .NET 3.5 since WriteAllLines is missing the appropriate overload). We need to select the string we want to write out, so I used the statement clients.Select(c => c.FullName) to retrieve the corrrect information.
Here’s the “program” so you can see what I’m working with if you want to follow the example.
class Client
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get { return FirstName + " " + LastName; } }
}
static void Main(string[] args)
{
var clients = new[]
{
new Client {FirstName = "John", LastName = "Doe"},
new Client {FirstName = "Jane", LastName = "Doe"}
};
Program program = new Program();
program.SaveClients(clients, "clients.txt");
}
The method for SaveClients works perfectly fine. But there’s one problem: it doesn’t belong to the appropriate class. Technically, that functionality belongs to IEnumerable<Client>, but it’s an interface and in the past you couldn’t put functionality on the interface. The way to save this problem with object oriented design is to create a ClientCollection and give it a Save method. But this forces use to use ClientCollection in lieu of IEnumerable<Client>. Although ClientCollection will implement IEnumerable, going to ClientCollection when necessary to access its functionality is cumbersome.
The other way of doing this is to create a helper class for IEnumerable<Client>. This provides logical cohesion, but still doesn’t make our code functional. We want functional cohesion, and extension methods will transform our helper class in such a manner as to provide it. Let’s make that class.
public static class EnumerableClientExtensions
{
public static void Save(this IEnumerable<Client> clients, string fileName)
{
File.WriteAllLines(fileName, clients.Select(c => c.FullName));
}
}
I renamed the method to Save. If the method is attached to IEnumerable<Client>, I see no reasons to specify that I’m saving clients. Let’s take a look at how I’m using this now in the new Main method of the program.
static void Main(string[] args)
{
var clients = new[]
{
new Client {FirstName = "John", LastName = "Doe"},
new Client {FirstName = "Jane", LastName = "Doe"}
};
clients.Save("clients.txt");
}
I believe that clients.Save(“clients.txt”) is much cleaner to read than SaveClients(clients, “clients.txt”). In addition, the Save method is now reusable without creating an unnecessary coupling. I still have one problem though: I do not like the File.WriteAllLines in the Save method. There’s something ugly about that as well. Reflecting upon what I find disturbing, I believe the smell is that I’m passing an IEnumerable instead of using the IEnumerable. So, let’s fix that as well.
public static class EnumerableClientExtensions
{
public static void Save(this IEnumerable<Client> clients, string fileName)
{
clients.Select(c => c.FullName).Write(fileName);
}
}
public static class EnumerableStringExtensions
{
public static void Write(this IEnumerable<string> strings, string fileName)
{
File.WriteAllLines(fileName, strings);
}
}
Now this is refactored as far as I can take it. The beautiful part is that you can write all IEnumerable<string> by calling the Write method within the method chain. This is what fluency is all about, a more fluid feel to your code, and it’s more readable!