Extension methods and members

C# · Advanced · 10 min

Extensions are how LINQ, ASP.NET Core setup (AddX/UseX) and fluent APIs are built.

An extension method is a static method in a static class whose first parameter has the this modifier. It can be called as if it were an instance method of that type:

public static class StringExtensions
{
    public static bool IsBlank(this string? s) => string.IsNullOrWhiteSpace(s);
}
// "  ".IsBlank()

It's only syntax: the compiler rewrites the call to StringExtensions.IsBlank(" "). Extensions can only access public members, are found only when their namespace is imported, and an instance method with the same signature always wins. Because the receiver can be null, extensions can be called on null references.

C# 14 extension blocks generalize this. Inside a static class, extension(Type receiver) { ... } can declare extension methods, extension properties, and static extension members on the type itself:

extension(string s) { public bool IsBlank => string.IsNullOrWhiteSpace(s); }

Typical uses:

  • Fluent setup APIs: builder.Services.AddMyFeature(), app.UseMyMiddleware().
  • Helpers on interfaces, giving every implementer shared behavior, as LINQ does for IEnumerable<T>.
  • Domain helpers on types you don't own.

Don't overuse them: extensions on object or very common types pollute IntelliSense everywhere, and behavior that needs private state belongs in the type itself.

Example

Console.WriteLine("  ".IsBlank());              // True
Console.WriteLine("hello world".WordCount());    // 2
Console.WriteLine(new[] { 3, 1, 2 }.SecondLargest()); // 2
Console.WriteLine("abc".Shout);                  // ABC!

public static class TextExtensions
{
    public static bool IsBlank(this string? s) => string.IsNullOrWhiteSpace(s);

    public static int WordCount(this string s)
        => s.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;

    public static T SecondLargest<T>(this IEnumerable<T> xs)
        => xs.OrderDescending().Skip(1).First();

    extension(string s)                          // C# 14 extension block
    {
        public string Shout => s.ToUpper() + "!"; // extension property
    }
}

Remember

  • Extension methods are static methods called with instance syntax
  • They are visible only when their namespace is imported
  • Instance members win over extensions with the same signature
  • C# 14 extension blocks add extension properties and static members

Watch out for

  • Writing extensions that need private state of the type; they can't access it.

Check yourself

  1. How is "x".IsBlank() compiled when IsBlank is an extension method?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods