csharp-metodos-extension

What are and How to Create Extension Methods in C#

  • 5 min

An extension method is a special static method, defined in a static class, but called as if it were an instance method of the type we are extending.

Imagine you are working with the system’s String class. Suddenly, you think: “I wish String had a .WordCount() method to count words directly”.

But you have a problem: You don’t have the source code for String. It’s a sealed (sealed) .NET class. You cannot inherit from it or modify it.

The “old” solution would be to create a StringHelper class with a static method:

int palabras = StringHelper.WordCount(miTexto); // Works, but it's ugly and breaks fluency

Copied!

C#‘s elegant solution is Extension Methods. They allow us to “inject” new methods into existing types without modifying them or inheriting from them.

Although they are very powerful, don’t go crazy creating extensions for everything (“Extension Method Hell”).

Syntax: The this modifier

To create an extension method, we need to follow three rules:

  1. The containing class must be static.
  2. The method must be static.
  3. The first parameter of the method must have the keyword this followed by the type we want to extend.

Let’s implement the word counter example:

using System;

public static class StringExtensions
{
    // Notice the 'this string str'
    public static int ContarPalabras(this string str)
    {
        if (string.IsNullOrWhiteSpace(str))
            return 0;

        return str.Split(new char[] { ' ', '.', '?' },
                         StringSplitOptions.RemoveEmptyEntries).Length;
    }
}
Copied!

Now any string in our project (provided we import the namespace) will have this method available:

string miFrase = "Hola mundo, esto es C#";

// Call as an extension method (Fluid and readable)
int n = miFrase.ContarPalabras();
Copied!

Under the hood it is syntactic sugar. The compiler does us the favor of translating our nice call into the static call without us knowing.

Extending interfaces

Where extension methods truly shine is not in extending concrete classes, but in extending Interfaces.

If you create an extension method for an interface, all classes that implement that interface will automatically gain that functionality.

This is the secret of LINQ. Methods like .Where(), .Select() or .First() are not defined in List<T> nor in Array. They are extension methods on the IEnumerable<T> interface.

Example: Let’s create a Print() method that works for any collection.

public static class CollectionExtensions
{
    // We extend IEnumerable<T>, so it works for List, Array, HashSet, etc.
    public static void Print<T>(this IEnumerable<T> coleccion)
    {
        Console.WriteLine("[" + string.Join(", ", coleccion) + "]");
    }
}

// Usage
int[] numeros = { 1, 2, 3 };
List<string> nombres = new List<string> { "Luis", "Ana" };

numeros.Print(); // [1, 2, 3]
nombres.Print(); // [Luis, Ana]
Copied!

Priority and conflicts

You might be wondering:

What happens if I create an extension method with the same name as a method that already exists in the class?

C# has a priority rule: Instance methods always win.

If the String class had a native ContarPalabras() method, our extension method would simply be ignored by the compiler, unless we call it explicitly in a static way.

Be careful not to “overwrite” future names. If Microsoft updates .NET and adds a method with the same name as your extension, your code will silently stop using your version and start using the native one (which might behave differently).

Null handling

Unlike normal instance methods, extension methods can be executed on null objects without immediately throwing an exception.

This happens because, remember, it’s actually a static call: StringExtensions.Metodo(null).

This allows us to create safe (“Null-safe”) check methods:

public static bool IsNullOrEmpty(this string str)
{
    return string.IsNullOrEmpty(str);
}

// Usage
string texto = null;
bool vacio = texto.IsNullOrEmpty(); // Doesn't crash! Returns true.

Copied!

If we had called texto.Length (an instance method), it would have thrown a NullReferenceException.

Practical examples