Extension Methods

Extension Methods

The Crash Course

  • Extension methods are a way to create a method that feels like it is part of a specific class (like, say, the string class) when you don't actually have access to that class to modify, and add the method.
  • Extension methods must be added in static classes, as static methods. The first parameter of the method is the type of the class that you want to add the extension method to, and it must be marked with the this keyword: public static class StringExtensions { public static string ToRandomCase(this string text) { /* Method implementation goes here… */ } }
  • Once an extension method has been created, you can call it as though it is a part of the class: string text = "Hello World!"; randomCaseText = text.ToRandomCase();
  • Extension methods are basically just syntactic sugar to make your code look cleaner. The C# compiler rewrites any time you use the extension method to directly call your static method.

Introduction

Let's say that you are using a class that someone else made. Perhaps one of the classes that comes with the .NET framework, like the String class (which happens to be the equivalent of the string type we've been using throughout these tutorials).

What if you have a method that you want to use on that type, but you don't have the class in front of you to modify it? For example, the String class or string type has a ToUpper() method and a ToLower() method, but what if we want to create a method to convert it to a random case, so each letter is randomly chosen to be upper case or lower case? (It makes it look a bit like a child wrote it.) Writing the method is relatively easy, but wouldn't it be nice if we could make it so that we can say something like "Hello World!".ToRandomCase();, just like we can do with "Hello World!".ToUpper();? But without having access to the class, we wouldn't normally have the ability to add our ToRandomCase() method as a new method in the class.

Normally.

But there's a way in C#. It is called an "extension method". Basically, we'll create a static method in a static class, and, along with yet another fancy use of the this keyword, we can make a method that we can use as though it were a member of the original class!

Creating an Extension Method

Creating an extension method is as simple as I just described; we'll make a static class, and put a static method in it that does what we want for the extension method.

So we start by adding a new class file, like we've done before. While it is not required, I typically call my class something like StringExtensions if I'm creating extension methods for the string class. And I usually make a separate class for each class that I'm making extension methods for… if that makes any sense. Basically, all of my extension methods for the string class would all go in the StringExtensions class, while all of my extension methods for, say, a Point class would go in a PointExtensions class, and so on. It helps them feel like they're still a part of the class, without actually being in the class.

In addition, we want to stick the static keyword on our class, to make the whole class a static class. So for starters, our class, without any extension methods in it, will look something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ExtensionMethods
{
    public static class StringExtensions
    {
    }
}

Since we haven't really talked about static classes much, it is worth briefly describing what that means. If a class is static, then all methods inside it must be static. Additionally, that means that you can't actually create an instance of a static class. (You can't say StringExtensions extensions = new StringExtensions();.) While that can be a bit restricting, those restrictions mean that the C# compiler and the .NET framework can handle it differently, in a way that is much better for performance.

Anyway, to define our actual extension method, we simply create our method as a static method, and the first parameter must be the type of object that we're creating the extension method for (string, in our case), marked with the this keyword, like this:

public static string ToRandomCase(this string text)
{
    // The method implementation will go here in a second...
}

We can indicate a return type, and in addition to the first parameter that is marked with the this keyword, we could also have any other parameters we want.

Like with operator overloading, and indexers, this is basically just syntactic sugar. The C# compiler will rework any place that we call our extension method into basically calling the extension method. So when we're done, we'll be able to say:

string title = "Hello World!"
string randomCaseTitle = title.ToRandomCase();

But the compiler will basically rework the code above to look like this:

string title = "HelloWorld";
string randomCaseTitle = StringExtensions.ToRandomCase(title);

But at any rate, what we end up with looks a lot nicer, and feels a lot more like it is a real method of the string class, which is a really nice thing.

But this is a double-edged sword. The method feels like it is a part of the original class, but it is not. If your extension method is in a different namespace than the original class, you may have problems where the class is recognized, but the extension method can't be found, or you may move from one project to another, only to discover that what you thought was a part of the original class turned out to be an extension method written by someone else, and you can no longer use it.

I'm not saying that you shouldn't use extension methods (quite the opposite, in fact, or I wouldn't be talking about it in this introductory set of C# tutorials) just that you need to be aware that sometimes, methods that appear to be a part of a class aren't actually a part of them, but rather, extension methods of the class.

On a related note, you may need to add a using statement for your extension method, depending on what namespace you stuck it in.

We can now finish up our example by completing the body of the ToRandomCase method:

string result = "";
 
for (int index = 0; index < text.Length; index++)
{
    if (random.Next(2) == 0)
    {
        result += text.Substring(index, 1).ToUpper();
    }
    else
    {
        result += text.Substring(index, 1).ToLower();
    }                
}
 
return result;

This goes through the original string, one character at a time, chooses a random number (0 or 1), and if it is 0, it makes it upper case, or if it is 1, it makes it lower case. So we end up with a random collection of upper and lower case letters, giving us the desired result.

So our complete code for the extension method class is this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ExtensionMethods
{
    public static class StringExtensions
    {
        private static Random random = new Random();
 
        public static string ToRandomCase(this string text)
        {
            string result = "";
 
            for (int index = 0; index < text.Length; index++)
            {
                if (random.Next(2) == 0)
                {
                    result += text.Substring(index, 1).ToUpper();
                }
                else
                {
                    result += text.Substring(index, 1).ToLower();
                }                
            }
 
            return result;
        }
    }
}

As we mentioned earlier, if your program is aware of the extension method, you can now do this:

string message = "I'm sorry, Dave.  I'm afraid I can't do that.";
Console.WriteLine(message.ToRandomCase());

We can use the extension method as though it is a part of the original class.

What's Next?

This is the last official tutorial in the C# Crash Course! We have one more tutorial to go through, that is sort of a wrap up of everything we've been discussing, and gives you some ideas on where to go next. I'd really recommend continuing on to that, since it is short and sweet. Then it's off to conquer the world with our new-found knowledge of C# programming!