Monday 10 August 2009

Forrays into Linq #2 - Effective use of Extension Methods

Today I'm talking a little bit about extension methods - where they come from, how to implement them and what I feel they are good for.

Extension methods were added to the .net framework to streamline native support for Linq. As you probably already know, Linq methods can be used on any object implementing the IEnumerable or IQueryable interfaces. What you may not have noticed is that the IEnumerable and IQueryable interfaces don't know anything about Linq. If you think about it, the easiest way to make objects that implement these interfaces accessible to Linq would have been to simply add method definitions to them. Then when a person creates a class that implements one or more of these interfaces they would simply create all the methods defined in the interface. Ctrl + '.' makes that a snap, no? Well it would if there weren't more Linq methods than you can shake a stick at. So, the clever people at Microsoft decided it would be best to expose the Linq methods in an entirely new way.

So, now that we have a new way of creating functionality how do we actually do it? Well, the syntax is simple if not entirely obvious in what it is actually doing. (Personally I feel that .net 3.5 has introduced a lot of ugliness into C# all in the name of Linq; one of the reasons I left C++ for C# was that there were less symbols cluttering up the code. Referencing and dereferencing pointers, accessing members and methods of an object through a pointer, and all that didn't make C++ particularly accessible. But I digress.) Let's say we want to add a method to the generic List class. I think it would be quite nice if we had a IsNullOrEmpty method like the static String method of the same name:


List<string> myListOfStrings;
if (myListOfStrings.IsNullOrEmpty())
return;

To do this we must first create a public, static class that houses our event methods - the name of the class is unimportant to the compiler but we should give it a good name so we know what's going on when we come back to it in 6 months time. Then we add a public, static method where the first parameter uses the keyword 'this':


public static class MyListExtensions
{
public static bool IsNullOrEmpty<T>(this List<T> thisList)
{
if (thisList == null || thisList.Count == 0)
return true;
return false;
}
}

A quick explanation of the method footprint: The first parameter is the instance of the object type you are extending. The 'this' keyword tells visual studio and the compiler that the method is an extension method for the type List. Next time you call up intellisense for a generic List object it will include this method with the (extension) tag. You can define as many parameters as you want after the 'this' parameter and have any return type. Interestingly the IsNullOrEmpty method on the String class is static because normally if you try to call a method, or access a property, on a null object you will get a NullReferenceException. This demonstrates that although extension methods look a lot like instance methods, they are fundamentally very different.

Some things to keep in mind when writing extension methods:

  1. Extension methods with the same footprint as existing methods will not get called. Try extending the Object class with a method called IsNullOrEmpty. If you use that method on the String type your extension method won't be called - the existing method will be.
  2. Create at least one separate namespace for your extension methods. It's probably worth subdividing your extensions namespace by type/functionality. That way you and your colleagues will only have to browse through the extension methods you are explicitly interested in while coding.

Personally I think extension methods are very useful in making code more readable. For instance, when we use Events one of the things we always have to do is check for subscribers. Ie:


if (MyEvent != null)
MyEvent(this, new EventArgs());

It's a pattern that is used everywhere but it really is ugly and not particularly human-readable. Now though, we can create an extension method on the EventHandler class and then write code that would perhaps look more like this:


if (MyEvent.HasSubscribers())
MyEvent(this, new EventArgs());

Much better!

No comments:

Post a Comment