A Closer Look at C# Extension Methods

Extension methods have been around in .NET for a long time and most developers use them daily without even realizing it. In this post, we're going to take a closer look at extension methods and how they work. Let's dive in!

Aaron Bos | Wednesday, July 19, 2023


What are Extension Methods

Before getting too deep, let's all get on the same page in regard to extension methods. I could try to summarize the documentation for you, but I think it's summed up pretty well in the first paragraph.

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type. For client code written in C#, F#, and Visual Basic, there's no apparent difference between calling an extension method and the methods defined in a type.

History of Extension Methods

I mentioned in the intro that extension methods have been around for a "long time". In fact, extension methods have been around since .NET 3, which was released in November 2007. The motivating factor behind the introduction of extension methods was LINQ. In order to make the LINQ API more natural, the methods were defined as extensions to the frequently used collection interfaces. This feature's quality of life improvement probably deserves more credit than it has been given. Can you imagine having to use a static method on IEnumerable<T> to call Select 🤮?

// Not great
IEnumerable.Select(someCollection, x => x == y);

// Chef's kiss
someCollection.Select(x => x == y);

Extension methods are all over .NET class libraries. So much so that we often use them without even realizing it. While many developers are frequent consumers of extension methods, not everyone writes them.

Creating Extension Methods

Extension methods are straightforward to create, but there are criteria that need to be met in order to be valid.

  1. The class that contains the extension method must be public and static.
  2. The extension method must be public and static (static is a given since it is a member of a static class).
  3. The first parameter of the extension method needs to be of the type that is being extended and needs to include the this modifier.
  4. Extension methods can't be the same name as an existing method on the type that is being extended.

Here is an example of an extension method.

public static class AaronExtensions
{
    public static bool AreYouAaron(this string str) 
    {
        return str?.Equals("Aaron", StringComparison.OrdinalIgnoreCase) ?? false;
    }
}

In code, we can then reference the AreYouAaron method on a string as if it were actually a method defined in the String class.

public class AaronFinder {
    static void Main() {
        var aaron = "Aaron";
        
        Console.WriteLine(aaron.AreYouAaron());
        Console.WriteLine("nigel".AreYouAaron());
    }
}

Extension methods are pretty neat and provide some interesting options for engineers when it comes to developing clean APIs. With that being said they are a little quirky with special requirements and syntax. Let's take a look at what happens "under the hood" once extension methods have been compiled.

Extension Methods Post-Compilation

Since extension methods "pseudo extend" an existing type, it's safe to assume that the C# compiler is performing some magic behind the scenes to make it all work. If we look at the decompiled extension method from earlier we'll notice a few things.

  1. The static class and method are defined with the [Extension] attribute
  • This tells the compiler that the class contains extension methods and that the member is an extension method respectively
  1. The this modifier is no longer included in the compiled code
  2. When the extension methods are actually called, they are called like normal static methods using AaronExtensions.AreYouAaron
[Extension] // 1. 
public static class AaronExtensions
{
    [System.Runtime.CompilerServices.NullableContext(1)]
    [Extension] // 1.
    public static bool AreYouAaron(string str) // 2. 
    {
        return str != null && str.Equals("Aaron", StringComparison.OrdinalIgnoreCase);
    }
}

public class AaronFinder
{
    private static void Main()
    {
        string str = "Aaron";
        // 3.
        Console.WriteLine(AaronExtensions.AreYouAaron(str));
        Console.WriteLine(AaronExtensions.AreYouAaron("Nigel"));
    }
}

Resources


ShareSubscribe
As always thank you for taking the time to read this blog post!