Extension methods in C#

Usually, when we want to write a method that processes a particular object, we would either write the method in the class of which the object is an instance, or else write a method in another class and pass the object in as a parameter. In the vast majority of cases, one of these techniques will be fine.

In some cases, however, we don’t have access to the source code for a class, and it would still be convenient to be able to call the method in the form object.method() rather than method(object). One such case where the former calling technique would be cleaner is if we wish to chain together a series of calls to ‘method’, with the returned value from one call being fed into the next call.

This sort of thing happens, for example, in data selection using LINQ. Don’t worry if you’ve never used LINQ; all you need to understand here is that LINQ allows us to make queries in an SQL-like manner, and these queries can be applied to pretty well any type of data source, even arrays and lists. In a query, we usually apply one or more filters to the data so we select only those items of data that match certain criteria. This is done using a call to a Where() method, where the arguments passed to the method are a collection of data and a predicate used as a filter condition. If we want to apply several filters in sequence to an initial set of data, then if we are restricted to using a ‘method(object)’ type of call, we would need to nest the calls in code something like ‘Where(Where(Where(originalData, Cond1), Cond2), Cond3)’. That is, we’d need to read the code from the inside out. It would be clearer (at least for humans to read) if we could write the call so we could read it from left to right, as in ‘originalData.Where(Cond1).Where(Cond2).Where(Cond3)’. Without extension methods, we couldn’t do this unless we had access to whatever class originalData belonged to, which often we don’t, since this data type could be some class written by someone else.

An extension method provides this functionality by allowing us to define a static method in a static class in a way that allows us to use it using syntax that looks like we’re using an instance method.

First, a quick review of the difference between static and instance methods. Whenever we create an object, we are creating an instance of a particular class. Non-static methods from that class can then be called with respect to that object. The syntax for this looks like object.InstanceMethod(parameters). In this case, the ‘object’ is implicitly passed to InstanceMethod() so its data fields are available to that method.

static method, on the other hand, is defined relative to a class rather than to any particular object created from that class. To call a static method, we write the name of the class followed by the method, as in ClassName.StaticMethod(parameters). No instances of ClassName need exist in order for a static method to be called.  In fact, C# won’t allow you to call an ordinary static method from an object; you must use the class itself to call one.

Except that is for an extension method. It’s easiest to see how they work by using an example. Suppose we want an extension to the ‘string’ class in C# that counts the number of vowels in a string, and we want to be able to call this method on a string ‘test’ by using the syntax ‘test.VowelCount()’. Clearly, since the provided ‘string’ class doesn’t have a method called VowelCount(), we can’t rely on the existing class to provide this functionality. But, aha, we might think, we can define a new class that inherits ‘string’ and add our VowelCount() method to that. This isn’t a good solution for two reasons. First, the ‘string’ class is sealed, which means that you aren’t allowed to inherit it. That alone scuppers our plan, but even if string weren’t sealed, the inheritance solution isn’t very clean since we’d need to make sure that every string in which we wanted to do a vowel count was of the derived type (or at least we’d need to cast each string to that type), and that’s kind of a hassle.

So how does an extension method solve our problem? First, we define a new, static class and write our extension method in it:

  public static class StringExt
  {
    public static int VowelCount(this string source)
    {
      string vowels = "aeiou";
      char[] lower = source.ToLower().ToCharArray();
      int count = 0;
      foreach (char c in lower)
      {
        if (vowels.IndexOf(c) > -1)
        {
          count++;
        }
      }
      return count;
    }
  }

This code converts the input string to lowercase and then to a char array, and then tests each char to see if it’s a vowel, incrementing a counter each time it finds one. However, notice a couple of things. First, the class in which this method is defined is static, as is the method itself. Second, notice that the string parameter that is passed to VowelCount() has the keyword ‘this’ in front of it. This is the trick that defines this method as an extension method. It means that the parameter with a ‘this’ can be placed before the method rather than inside the parentheses, thus making the method look like it’s being called as an instance method on this parameter. (We can provide other parameters to an extension method as well, but only the first parameter will have a ‘this’ in front of it. The other parameters are passed inside the parentheses as usual.)

To call this method, we could use code like this:

    static void Main(string[] args)
    {
      string test;
      do
      {
        Console.Write("Enter a string (quit to finish): ");
        test = Console.ReadLine();
        if (test.Equals("quit"))
        {
          break;
        }
        Console.WriteLine(test + " has " + test.VowelCount() + " vowels.");
      } while (true);
    }

This reads a sequence of strings from the console and calls VowelCount() on each one, then prints out the results. Note that we call VowelCount() as if it were an instance method of a ‘string’. In Visual Studio, Intellisense is bright enough to recognize extension methods, so when you type ‘test’ followed by a dot, VowelCount() will appear in the list of options.

That’s really all there is to defining and using extension methods. As Microsoft’s own documentation points out, however, they should be used sparingly, since the usual technique of inheritance (or just adding an instance method to the class itself if you have access to the source code) is a better design in most cases in which the base class isn’t sealed.

There are a couple of rules worth pointing out before we leave the topic.

First, if an instance method with the same signature as the extension method already exists, the instance method will always get called, and the extension method will never get called. The ‘same signature’ means that the parameter lists of the two methods are the same, except that the extension method has an extra first parameter which is the object on which the method is called. For example, the ‘string’ class contains a CompareTo(string other) instance method which is called using the syntax ‘source.CompareTo(other)’ where source and other are the two strings being compared. If we defined an extension method with the same signature, it would look like ‘CompareTo(this string source, string other)’. If we did do this, the extension version of CompareTo() would never get called, however.

The other thing worth noting is that we can define an extension method with the same name as a C# property. For example, ‘string’ has a Length property that returns the length of the string. We could (although I can’t think why we would) define a Length extension method with the signature ‘Length(this string source)’ and then call this method on a string using the syntax ‘myString.Length()’. Adding the empty parentheses after Length will result in the extension method rather than the property being called. Thus an extension method with no parameters (apart from the object itself) is not the same thing as an alternative definition for a property.

Advertisements
Post a comment or leave a trackback: Trackback URL.

Trackbacks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: