Extension methods C# 3.0 and VB9

Sun, February 18, 2007, 06:43 PM under dotNET | Orcas | LINQ
How many times have you written wrapper methods for objects that in reality you wished were part of the object itself? For example, if you find yourself checking in multiple places if a string is all uppercase, you may write a wrapper method for it, e.g.:
namespace Helper
{
public static class StringHelper
{
public static bool IsAllUpper(string s)
{
//implementation left as an exercise to the reader
}
}
}
...or in VB if you prefer:
Namespace Helper
Public Module StringHelper
Public Function IsAllUpper(ByVal s As String) As Boolean
'implementation left as an exercise to the reader
End Function
End Module
End Namespace
...which you then call like this (assuming the Helper namespace is in scope):
string s = ...
bool itIs = StringHelper.IsAllUpper(s);
In VB:
Dim s As String = ...
Dim itIs As Boolean = StringHelper.IsAllUpper(s)
...when really, what you want is something more readable like this:
bool itIs = s.IsAllUpper();
In VB:
Dim itIs As Boolean = s.IsAllUpper()
That is exactly what extension methods let you do. To achieve that, a new attribute has been introduced: System.Runtime.CompilerServices.ExtensionAttribute (in System.Core.dll).

In the VB example above, simply add the Extension attribute to the method in the module:
    <Extension()> _
Public Function IsAllUpper(ByVal s As String) As Boolean
...
In c#, there is a shortcut to using the attribute which makes the following two represent the same thing:
[Extension()]
public static bool IsAllUppercase(string s) {...}

public static bool IsAllUppercase(this string s) {...}
...except the C# compiler will not let you write the first version and will instruct you to use the "this" keyword instead (a bit like it doesn't let you write an explicit finalizer method and instead instructs you to use destructor syntax).

So, back to our example, if in the c# case we simply insert the this keyword before the string argument in the IsAllUpper helper method (and in the VB case insert the Extension attribute before the method) then we can indeed type s.IsAllUpper() and the compiler is happy. In fact, the advantage of doing this is that intellisense will help you by showing the method when you type a dot after the variable thus making the helper method much more discoverable than what it would be today - see the following screenshot for how it distinguishes extension methods from normal methods by sticking an arrow in front:


Please note the following points which hopefully help answer any questions you might have:
0. It is not by accident that I chose a static class in C# and a module in VB. Those are the only places where you can define extension methods.
1. The compiler generates the longhand version: no magic is taking place here at runtime. Since you are not *really* adding a method to the class, you can only access public members of the object (e.g. only public members of the string in our example) from your extension method. It also follows that extensions are available to subclasses of the class you are extending.
2. For an extension method to be visible/applicable/in scope, you must import the namespace it resides in. So in our example, if you do not stick a using Helper; (or in VB Imports Helper) at the top of the file where you make the call, the extension method would not show up in intellisense and would not compile.
3. If two namespaces with extensions methods that have the same name are brought into scope, a compile error occurs; hence the usefulness of the previous point.
4. If you add to the object a real method that has the same name as the extension method, the real method takes precedence and your extension method is silently ignored.
5. Extension methods for properties are not possible.

Overall, I like this feature *a lot* (will post some concrete examples of why in the future). My only worry is that some devs will get lazy and use extension methods rather than inheritance when the latter is more applicable... time will tell.
Comments are closed.