Continuing in our weekly blog post series that highlights a few of the new additions to the Framework Design Guidelines 2nd edition.. This content is found in the Extension Methods section of Chapter 5: Member Design. One of the major new features we added to the Framework recently is extension methods. While this a very powerful new feature, it does come with some new responsibility.
CONSIDER using extension methods in any of the following scenarios:
- To provide helper functionality relevant to every implementation of an interface, if said functionality can be written in terms of the core interface. This is because concrete implementations cannot otherwise be assigned to interfaces. For example, the LINQ to Objects operators are implemented as extension methods for all IEnumerable<T> types. Thus, any IEnumerable<> implementation is automatically LINQ-enabled.
- When an instance method would introduce a dependency on some type, but such a dependency would break dependency management rules. For example, a dependency from String to System.Uri is probably not desirable, and so String.ToUri() instance method returning System.Uri would be the wrong design from a dependency management perspective. A static extension method Uri.ToUri(this string str) returning System.Uri would be a much better design.
The value of this cannot be overstated. Extension methods provide you with a way to give interfaces not just one default implementation but as many as you need, and you can choose between them simply by bringing the right namespace(s) into your lexical scope with using. However, this could be easily abused in the same way that complex #include combinations in C++ can make the final code hard to read.
Another interesting feature of this manner of adding functionality to a class is that you could potentially partition (extension) methods of the same class into different assemblies for performance reasons. Again, this should not be done lightly, because you could cause great confusion.
This is one of the many times I like to quote from Spiderman – “With great power comes great responsibility.”
Section 188.8.131.52 covers the topic of layering in namespaces, which I think applies well to extension methods.
One scenario I’ve seen is to put functionality that is more advanced or esoteric in a separate namespace. That way, for core scenarios, these extra methods do not “pollute” the API. But for those who know about the namespace, or need these alternate scenarios, they can add the namespace and gain access to these extra methods.
The drawback, of course, is that this results in a “hidden” API that is not very discoverable.
Extension methods can also be used to provide actual concrete method implementations for interfaces. For example, if you ship an IFoo interface, you can also have a static Foo class with a method public static void Bar(this IFoo f) that makes all instances of IFoo effectively have an extra Bar method.