X

Extension methods in C#

Extensions method introduced with C# 3.0 make it possible to “extend” existing classes with new methods without access to source code of those classes. This blog post is deeper technical introduction to extension methods for those who are making decisions about software design and architecture.

How extension methods work

Extension methods have some differences from regular methods. They can be members of static classes only and their argument list must have one specific parameter that specifies type they are extending. Take a look at the following sample of very dummy extension method.

public static class MyExtensions
{
    public static string Dummy(this string str)
    {
        return str;
    }
}

Keyword this in argument list specifies that this extension method is for strings. If we have string s and the previous extension method then we will see something like this in Visual Studio when typing point after s.

Notice that Visual Studio informs us – it is extenion method. Before going deeper to internals of extension methods let’s see some practical ones.

Examples of extension methods

To get more realistic I will show here some real extension methods.

public static class StringExtensions
{
    public static string Nl2Br(this string s)
    {
        return s.Replace("\r\n", "<br />")
                .Replace("\n", "<br />");
    }

    public static string Repeat(this string instr, int n)
    {
        if (string.IsNullOrEmpty(instr))
        {
            return instr;
        }

        return new StringBuilder(instr.Length * n)
                        .Insert(0, instr, n)
                        .ToString();
    }

    public static string MD5(this string s)
    {
        var provider = new MD5CryptoServiceProvider();
        var bytes = Encoding.UTF8.GetBytes(s);
        var builder = new StringBuilder();

        bytes = provider.ComputeHash(bytes);

        foreach (byte b in bytes)
        {
            builder.Append(b.ToString("x2").ToLower());
        }

        return builder.ToString();
    }
}

.NET Framework has many extension methods available in System.Linq namespace, by example.

LINQ without extension methods

To demonstrate how ugly things can be without extension methods I take a simple LINQ query.

var invoices = _context.Invoices.Where(i => i.Customer.Id == 1)
                                .OrderByDescending(i => i.Date)
                                .Select(i => new { Date = i.Date, Total = i.Total })
                                .ToList();

All it does is selecting invoices for given customer, ordering these by invoice date in descending order and returning list of dates and totals for invoices. Notice that Where(), OrderByDescending(), Select() and ToList() are all extension methods.

Let’s play that extension methods doesn’t exist and we have to call these methods like any other static methods.

var query = Enumerable.Where(_context.Invoices, i => i.Customer.Id == 1);
query = Enumerable.OrderByDescending(query, i => i.Date);
var query2 = Enumerable.Select(query, i => new { Date = i.Date, Total = i.Total });
var invoices = Enumerable.ToList(query2);

Too easy to read and not enough ugly? Okay, let’s add here nested version.

var invoices = Enumerable.ToList(
                    Enumerable.Select(
                        Enumerable.OrderByDescending(
                            Enumerable.Where(_context.Invoices, i => i.Customer.Id == 1),
                            i => i.Date),
                        i => new { Date = i.Date, Total = i.Total }
                    )
                );

The nested version makes my eyes bleed and I better return to first version with nice extension methods. Wanna join me?

How to use extension methods?

Extension methods are not silver bullets and they don’t give any access to internals of class they are applied to. Extension methods cannot access protected and private members of class. If they are defined in separate assembly then they don’t have access also to internal members of class.

  • use extension methods only for extending public surface of class, don’t make internals available to extension methods
  • if access to protected members is needed then better use inheritance
  • don’t create extension methods you probably need to call only once in application code
  • if your extension methods are general enough to use them in multiple projects then consider creating a separate library for them
  • keep extension methods at some upper namespace in hierarchy because otherwise they are hard to find for other developers

Finally, before adding new extension method it is good idea to stop for a moment and think if it should be extension method or something else.

Internals of extension methods

Now it’s time to ask – how are extension methods implemented at .NET Framework level? For extensions methods C# compiler adds automatically ExtensionAttribute. This attribute specifies that given method is extension method. Consumers of this attribute are usually development tools like IDE-s, code generators, etc.

To see how extension method looks after compiling let’s write simple piece of code using string.Repeat() method shown above.

static void SayYes()
{
    var yes = "Yes!".Repeat(3);

    Console.WriteLine(yes);
}

After building the code and opening program with disassembler we can see the following IL code.

.method private hidebysig static void 
SayYes() cil managed 
{
.maxstack 8

// [25 7 - 25 42]
IL_0000: ldstr        "Yes!"
IL_0005: ldc.i4.3    
IL_0006: call         string StringExtensions::Repeat(string, int32)
IL_000b: call         void [System.Console]System.Console::WriteLine(string)
IL_0010: ret         

} // end of method Program::SayYes

It defines stack size for method and loads values “Yes!” and 3 to memory. Then it calls Repeat() method on string “Yes!” and writes the result to console.

Notice how Repeat() method is called. It is called like any other static method – no single sign about Repeat() method being something special. If it is regular static method call at IL level then let’s try to call Repeat() method same way in our sample code.

static void SayYes()
{
    var yes = StringExtensions.Repeat("Yes!", 3);
   
    Console.WriteLine(yes);
}

This code compiles with no errors and in IL it gives exactly the same result as before.

Wrapping up

Extension methods are methods that are displayed as a members of class instance by development tools. They enable us only to “extend” public surface of class. Extension methods doesn’t see inside the classes they are applied to. Extension methods are internally static methods of some static class. Methods are decorated with ExtensionAttribute to tell development tools that these methods use keywords “this” with their first argument. In all other means extension methods are like all other methods. Adding new extension method is the matter of code design. Performance-wise they are equal to all other static methods that make same work.

Liked this post? Empower your friends by sharing it!
Categories: C#

View Comments (3)

Related Post