Nifty extension methods

One of my favorite features in .Net 3.5 is the ability to extend a class using extension methods. Earlier you could only extend a class either by creating a new class that inherits from the first class or you could create a partial class. Of course if you have the source code of the original class you could just write the new code directly in it and recompile it. In many cases you don’t have access to the source code of a class and if the class is also sealed (NotInheritable in VB) you would not be able to extend its behavior using the other methods either. The string class is a typical example of a class that is sealed and you normally wouldn’t be able to extend at all. That is until extension methods entered the scene.

In VB extension methods must reside inside a module and be decorated with the Extension attribute (available in the System.Runtime.CompilerServices namespace), in C# they need to be defined in a static class and use the this keyword on the first parameter (the method does not have to be decorated with the Extension attribute in C#).

Let say that you want to extend the string class with a method that allows you to do a case-insensitive comparison with another string. Many programmers use the ToUpper or ToLower methods on the strings before comparing them, but that doesn’t always give the correct result and you should avoid doing that. The reason is that these methods are not always culture aware. Take the ß (the sharp S or double S in German) character for example. It is considered to be a lower case character which in upper case should be written as two S’es. Straße (street) versus STRASSE, but calling ToUpper on the word Straße does not return STRASSE, so comparing the two spellings using the ToUpper or ToLower methods will fail. A much better approach would be to use the CaseInsensitiveComparer class (from the System.Collections namespace).

OK, so let’s look at the code for what that extension method could look like.

Imports System.Collections.Generic
Imports System.Runtime.CompilerServices

Public Module ExtensionMethods
<Extension()> _ Public Function EqualsText(ByVal str As String, ByVal equalsTo As String) As Boolean Dim sc As New CaseInsensitiveComparer Return (sc.Compare(str, equalsTo) = 0) End Function End Module

As you can see it simply looks as a small helper function that accepts two strings and returns a boolean. The thing is that when you use this as an extension method, the first parameter type tells the compiler what kind of object you are extending. So when you call it you would only pass in one parameter as you can see in the image below.


Enough introduction show some more nifty extension methods

One of the top features in VB9 is that you can work with XML literals, in my opinion one of the best features since IntelliSense was introduced back in VB5. Even if you don’t work much with XML (does people like that still exist?) you can still use them in your source code. Take this simple code for example:

Dim sql As String = <query>select CustomerName from customer where CustemerID=@CustomerID</query>.Value

Here I actually create an XElement object using an XML literal, and then convert it to a string by calling the Value method, which returns the inner value of the element. OK so what’s the benefit of doing that instead of simply putting the text inside quotes? Well, in the above example there are no benefits at all, but your query is probably much longer than what’s shown above.

Dim sql As String = <query>
                      update customer set
                      where CustomerID=@CustomerID

OK, the above is still a very straight forward query but the idea here is that it’s much easier to read the query when you have it on different lines which you can’t do in VB without doing a lot of string concatenations. The problem with the above is that all whitespaces are contained within the string and there are several spaces at the beginning of each line. So why not write an extension methods that trims off the spaces?

    <Extension()> _
    Public Function TrimmedValue(ByVal xml As XElement) As String
        Return String.Join(vbLf, (String.Join(vbLf, ( _
             From s In xml.Value.Split(New Char() {Convert.ToChar(10)}) _
             Select s.Replace(vbTab, " ").Trim()).ToArray)).Split( _
             New Char() {Convert.ToChar(10)}, StringSplitOptions.RemoveEmptyEntries))
    End Function

Now you can use the TrimmedValue extension method instead of the Value method. Please note that you need to import the System.Xml.Linq namespace to work with XML literals.

Generic types

Extension methods also works with generics. Say you want to paginate an IEnumerable(Of T). There are actually already extension methods available to do this, they are called Skip() and Take().

Dim page = someIEnumerableType.Skip(30).Take(15)

The above will skip the first 30 elements and return the next 15. But let us combine that into one single call.

    <Extension()> _
    Public Function Paginate(Of T)(ByVal list As IEnumerable(Of T), _
                                   ByVal skip As Integer, _
                                   ByVal take As Integer) As IEnumerable(Of T)
      Return list.Skip(skip).Take(take)
    End Function

It’s now easy to paginate anything that implements the IEnumerable(Of T), which means any array or collection.

    Dim names As String() = {"Anders", "Bart", "Ceasar", "David", _
                             "Eric", "Fabian", "Gunnar", "Harald", _
                             "Ingemar", "Joacim", "Karl", "Lennart", _
                             "Martin", "Nick", "Olaf", "Peter", "Quintus", _
                             "Rolf", "Stephen", "Thor", "Ulf", "Valdemar", _
                             "Wiktor", "Xerxes", "Yngve", "Zach"}
    Dim page = names.Paginate(10, 5) 'returns "Karl" through "Olaf"

If you have used LINQ you probably familiar with anonymous types. They are a great way of creating a new object without first having to type the source for it, since the compiler takes care of that for you. But one of the problems with anonymous types is that they are nameless which makes it hard to create a new one of the same type or to create a list of them if you only have a single instance. However using generics and a simple extension method you can turn any type into a list. Deborah Kurata recently posted this code on her blog that demonstrated how you would do that.

    <Extension()> _
    Public Function ToList(Of T)(ByVal type As T) As List(Of T)
      Return New List(Of T)
    End Function

Of course that would only create a list of a specific type, it would not add anything to the list. If you like you could extend the code by adding the value to the list.

    <Extension()> _
    Public Function ToList(Of T)(ByVal type As T) As List(Of T)
      Dim list = New List(Of T)
      Return list
    End Function

So what if you want to test if a type is Nullable?

    <Extension()> _
    Public Function IsNullable(Of T)(ByVal obj As T) As Boolean
      If obj Is Nothing Then
        'if it is Nothing already, it must obviously be nullable
        Return True
      End If
      Dim type = GetType(T)
      If Not type.IsValueType Then
        'if it's not a value type it must be a reference type and reference types are always nullable
        Return True
      End If
      'Nullable.GetUnderlyingType returns Nothing if the type passed to it isn't Nullable
      Return (Nullable.GetUnderlyingType(type) IsNot Nothing)
    End Function
More ideas

As you probably have noticed most of the above examples are pretty short helper functions that are turned into extension methods. The advantage is that you can use the object oriented dot syntax and get IntelliSense to help you make sure you spelled your code correctly. Of course there is no rule that states that an extension method must be brief, but I find that they usually are.

So what more can you do? How about this:

  • Extend a StringBuilder with a ToString method that accepts a delimiter and returns a string that is a delimited list of each line in the StringBuilder.
  • If you often need to store user inputted data in a database, maybe you should extend the String class to have a ToSecureSqlString that for example escapes all single quotes (using two single quotes).
  • Create an extension to the Control class that can do a recursive search for a child control.

You will soon notice that you have quit a large toolkit of nifty extension methods ready to be used in any of your projects.

Have fun!

kick it on

2 thoughts on “Nifty extension methods

  1. I like your IEnumerable.Paginate extension. Think it would be better served to have a signature like (C#, sorry):

    public static IEnumerable Paginate( this IEnumerable list, int pageSize, int page )
    return list.Skip( pageSize * ( page – 1 ) ).Take( pageSize );

  2. Great idea Terry, thanks for sharing.

    BTW I don’t mind the C#. Some people like a Cup while others like a Cup(Of T) but in the end it’s the same beverage. 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *