(Print this page)

Extension methods in .Net
Published date: Thursday, January 26, 2012
On: Moer and Éric Moreau's web site

When you are using the API of any classes or controls, you always come up with this question: why isn’t MethodX exists? The answer is simple; the authors of the component you are using just cannot implement every single method you think of.

Since programming languages exist, developers always wanted to fit the component to their usage. For a long time, inheritance was THE answer but not all objects allow that (just think of the String here which was created as NotInheritable / Sealed). Starting with .Net 3.5, Visual Studio .Net offers developers using one of these classes to add their own add-ons to existing code without using inheritance. The creators of the classes cannot prevent you from doing it. And finally, you don’t need the original code.

The mechanism is simple, it is called Extensions methods. You would be surprised to see how many .Net developers are not using this mechanism yet.

I am not good at patterns but I have been told that this is the Visitors patter which allows us to change the class structure without changing the actual class. It is a way of separating the logic and algorithm from the current data structure.

This month demo

The downloadable demo this month is available in both VB and C#. It was created using Visual 2010 but you can reuse this technique using Visual Studio 2008 (and above).

What you need

The big prerequisite is to use .Net Framework 3.5 (and to target this framework). It is working in both VB and C#.

There is a hack that will let you, if you really want to, use extension methods in Visual Studio 2008 targeting the .Net Framework 2.0. I will let you use your preferred search engine to discover how to do it.

Where do we start?

We will start by creating a simple extensions method. This method will just reverse a string. This method has nothing special except that we want to use it as any other method we use on a string (just like the ToUpper and ToLower methods) by using the MyString.MyMethod syntax.

If you are a VB programmer, you need to know that the extensions need to be stored in a Module and you will need to qualify your method with an attribute much like this:

Namespace MyExtensionsNamespace

    Module Extensions

        <System.Runtime.CompilerServices.Extension()>
        Public Function Reverse(ByVal pString As String) As String
            Dim charArray(pString.Length - 1) As Char 
            For i As Integer = 0 To pString.Length - 1
                charArray(i) = pString(pString.Length - i - 1)
            Next
            Return New String(charArray)
        End Function

    End Module

End Namespace

If you are a C# developer, your will create a static class (because C# does not have the equivalent of a Module) and a static method. Notice that there is no attribute here but rather your first parameter will be prefix with this. Your code will look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyExtensionsNamespace
{
    public static class Extensions
    {

        public static string Reverse(this String pString)
        {
            char[] charArray = new char[pString.Length];
            int len = pString.Length - 1;
            for (int i = 0; i <= len; i++)
            {
                charArray[i] = pString[len - i];
            }
            return new string(charArray);
        }

    }
}

As you can see in both VB and C#, the method is declared with a single argument but we will never pass that argument. Really what we want is to use something like MyStringVariable.Reverse() and this is exactly what will happen. The compiler will do some magic with the attribute (for VB) and with “this” (for C#). It will become clearer when we will use it.

About the NameSpace

It would have been too easy for me to bypass the namespace. Very often, your extension methods will be stored in a standalone component (.dll) because you will want to reuse them through all your projects. And very often, a different component means a different namespace. In that case, before being able to use the extension method in another project, you will need to (first) add a reference to that component and (second) add the Imports (or using for C#) statement at the top of the file in which you want to use the extensions.

This is why at the top of my form, you will see:

Imports DemoExtensionsVB.MyExtensionsNamespace

Or for C#:

using MyExtensionsNamespace;

Using the extension method

Now that we have everything settled, we can use it. This method (which was declared with an argument) will be shown as a regular method on any string object (as long as the imports or using statement is at the top of the file). As you can see in Figure 1, our Reverse extension method is shown with a little blue arrow meaning that it is an extension method. You can also see that there are other blue arrows in that list that you didn’t create. These other methods are really extension methods created by Microsoft, and these are coming from Linq which was the reason why the extensions methods where added to the language.

Figure 1: the Intellisense showing our extension method



So your utilisation of that new method in a VB class would look like this:

Private Sub Button1_Click() Handles Button1.Click
    Dim s As String = Button1.Text
    MessageBox.Show(s.Reverse)
End Sub

While C# developers would write something this:

private void button1_Click(object sender, EventArgs e)
{
    string s = button1.Text;
    MessageBox.Show(s.Reverse());
}

As you can see, our very own method can now be called just like any other intrinsic method of the String class. Isn’t it cool?

Now if you have a variable of any type other String (an Integer for example), you will not see the Reverse method showing in the Intellisense. Do you remember the first and only argument of our method? It was a String. This argument dictates on which data type this new method will be available.

Extension methods with other arguments

You are now probably asking yourself what would happen if you try to add other arguments to an extension method. It is without a surprise that … it is working.

Check this extension method:

<System.Runtime.CompilerServices.Extension()>
Public Function ReplaceCaseInsensitive(ByVal pString As String,
                                       ByVal pOldValue As String,
                                       ByVal pNewValue As String) As String
    'Ignore case replace
    Return Regex.Replace(pString, pOldValue, pNewValue, RegexOptions.IgnoreCase)
End Function

The first parameter dictates the data type that will offer this new method (and also receive the value of before the .).

This specific method can be used like you would do for any other method:

s = "This a dummy sentence"
MessageBox.Show(s.ReplaceCaseInsensitive("DUMMY", "Dummy"))

Conclusion

It is not because you can do something that you should do it. My rule of thumb regarding the extension methods is that I never add extensions to a class I have built myself. In this situation, I reopen the code of the class and add the new method directly there.

I only create extension methods for classes I am not the creator.


(Print this page)