Extension Methods

Reviewed & published by Brayan K

What if you could add a method to string — a type you didn't write and can't change? Extension methods let you do exactly that. They're how LINQ bolts Where and Select onto every collection, and they're your tool for turning awkward helper calls into clean, readable, fluent code that reads like the method always belonged there.

Part of the free C# course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn

💡 Real-World Analogy

Think of a phone case with a built-in bottle opener. You didn't redesign the phone — the manufacturer can't and won't add a bottle opener — but the case snaps on and gives your phone a new ability that feels built-in. Extension methods snap new abilities onto existing types the same way: the original type is untouched, yet at the call site it looks like the method was always there.

1. Writing Your First Extension Method

Three rules: the method must be in a static class, the method must be static, and the first parameter must have the this modifier naming the type you're extending. That's it — now "hello".Shout() works, even though string has no Shout method. It's pure syntactic sugar for a static call.

using System;

// Extension methods live in a STATIC class and are STATIC methods.
// The 'this' keyword on the first parameter is the magic — it says
// "this method extends the string type".
static class StringExtensions
{
    public static string Shout(this string text) => text.ToUpper() + "!";
}

class Program
{
    static void Main()
    {
        string greeting = "hello";

        // Even though string has no Shout method, we can now call it
        // AS IF it were built in:
        Console.WriteLine(greeting.Shout());   // HELLO!

        // It's just syntactic sugar for:
        Console.WriteLine(StringExtensions.Shout(greeting)); // HELLO!
    }
}

// ✅ Expected output:
//    HELLO!
//    HELLO!

2. Extensions with Parameters

Anything after the this parameter is a normal argument. So age.IsBetween(18, 65) reads naturally while still being a plain static method underneath. You can extend any type — int, double, your own classes, even interfaces.

using System;

static class NumberExtensions
{
    // Extension methods can take extra parameters after 'this'.
    public static bool IsBetween(this int value, int low, int high)
        => value >= low && value <= high;

    // 'this' can extend any type — here, double:
    public static double RoundTo(this double value, int places)
        => Math.Round(value, places);
}

class Program
{
    static void Main()
    {
        int age = 25;
        Console.WriteLine(age.IsBetween(18, 65));   // True
        Console.WriteLine(age.IsBetween(30, 40));   // False

        double pi = 3.14159;
        Console.WriteLine(pi.RoundTo(2));            // 3.14
    }
}

// ✅ Expected output:
//    True
//    False
//    3.14

Your turn — write a Reversed() extension on string and call it.

using System;

static class StringExtras
{
    // 🎯 YOUR TURN — replace each ___ then press "Try it Yourself".

    // 1) An extension that reverses a string.
    //    Hint: new string(text.Reverse().ToArray())  needs System.Linq,
    //    but here we'll build it with a simple loop for clarity.
    public static string Reversed(this string ___)   // 👉  text
    {
        char[] chars = text.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }
}

class Program
{
    static void Main()
    {
        // 2) Call your new method on a string literal
        Console.WriteLine("stressed".___);    // 👉  Reversed()

        // ✅ Expected output:  desserts
    }
}

3. Extensions on Collections (the LINQ pattern)

Extension methods on IEnumerable<T> are exactly how LINQ works. You can write your own and chain them right alongside Where and Select. Here's a custom MedianOrZero() that slots straight into a LINQ pipeline.

using System;
using System.Collections.Generic;
using System.Linq;

static class CollectionExtensions
{
    // Extension methods are how LINQ itself works — Where, Select, etc.
    // are all extension methods on IEnumerable<T>. Here's a custom one:
    public static double MedianOrZero(this IEnumerable<int> source)
    {
        var sorted = source.OrderBy(x => x).ToList();
        if (sorted.Count == 0) return 0;
        int mid = sorted.Count / 2;
        return sorted.Count % 2 == 1
            ? sorted[mid]
            : (sorted[mid - 1] + sorted[mid]) / 2.0;
    }
}

class Program
{
    static void Main()
    {
        var numbers = new List<int> { 7, 1, 5, 3, 9 };

        // Chain our custom extension right alongside built-in LINQ:
        var result = numbers.Where(n => n > 2).MedianOrZero();
        Console.WriteLine(result);   // 7   (from {7,5,3,9}, sorted {3,5,7,9}, median 6)

        Console.WriteLine(new List<int>().MedianOrZero()); // 0  (empty, safe)
    }
}

// ✅ Expected output:
//    6
//    0

🧩 Reorder Challenge

These lines define and use a Doubled() extension, but they're out of order. Arrange them so the program compiles and prints 14.

A.     public static int Doubled(this int n) => n * 2;
B. static class IntExtensions {
C.     int result = 7.Doubled();
D. }
E.     Console.WriteLine(result);
B. static class IntExtensions {
A.     public static int Doubled(this int n) => n * 2;
D. }
// ...inside Main:
C.     int result = 7.Doubled();
E.     Console.WriteLine(result);

// Output:  14

The static class wrapping the static this-method must be declared before the call site can use 7.Doubled().

🧠 Quick Recall

1. What does this print?

static class E { public static int Sq(this int n) => n * n; }
Console.WriteLine(6.Sq());

36 — 6.Sq() calls the extension, returning 6 * 6.

2. What does this print?

static class E { public static string Twice(this string s) => s + s; }
Console.WriteLine("ab".Twice());

abab — the extension concatenates the string with itself.

3. True or false: an extension method can override a real instance method of the same name.

False — instance methods always win; the extension is only used when no instance method matches.

Putting It Together: a Validation Toolkit

Real teams keep call-sites readable by grouping little helpers as extensions. Here's a tiny string-validation toolkit you'd genuinely use.

using System;

// A small, readable validation toolkit built from extension methods.
// This is how real codebases keep call-sites clean.
static class ValidationExtensions
{
    public static bool IsValidEmail(this string s)
        => !string.IsNullOrWhiteSpace(s) && s.Contains('@') && s.Contains('.');

    public static string OrDefault(this string? s, string fallback)
        => string.IsNullOrWhiteSpace(s) ? fallback : s;

    public static string Truncate(this string s, int max)
        => s.Length <= max ? s : s.Substring(0, max) + "...";
}

class Program
{
    static void Main()
    {
        Console.WriteLine("user@site.com".IsValidEmail()); // True
        Console.WriteLine("not-an-email".IsValidEmail());  // False

        string? name = null;
        Console.WriteLine(name.OrDefault("Anonymous"));    // Anonymous

        Console.WriteLine("This is a long description".Truncate(10)); // This is a ...
    }
}

// ✅ Expected output:
//    True
//    False
//    Anonymous
//    This is a ...

Pro Tips

Common Errors (and the fix)

Mini-Challenge: a WordCount Extension

Add a WordCount() method to string — the kind of small helper you'll reuse forever. Declare the static class, write the method, and call it.

using System;

class Program
{
    static void Main()
    {
        // 🎯 MINI-CHALLENGE: a WordCount extension
        // 1. Above Program, declare:  static class TextExtensions { ... }
        // 2. Add:  public static int WordCount(this string text)
        //    that returns how many words are in the string.
        //    Hint: text.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length
        // 3. Call "the quick brown fox".WordCount() and print it.
        //
        // ✅ Example output:
        //    4

        // your code here
    }
}

🎉 Lesson Complete

Practice quiz

Where must an extension method be defined?

  • In any class
  • In an interface
  • In a static, non-nested class
  • In the type it extends

Answer: In a static, non-nested class. Extension methods must live in a static, non-generic, non-nested class (CS1109 otherwise).

What makes a static method an extension method?

  • The 'this' modifier on the first parameter
  • The [Extension] attribute
  • Being named with 'Extension'
  • Returning the same type it extends

Answer: The 'this' modifier on the first parameter. The 'this' modifier on the first parameter is the magic that turns an ordinary static method into an extension method.

The call `value.Method()` on an extension is just sugar for what?

  • value.GetMethod()
  • new Method(value)
  • Method.Invoke(value)
  • Class.Method(value)

Answer: Class.Method(value). value.Method() is syntactic sugar for the plain static call Class.Method(value).

Do extension methods modify the original type's source code?

  • Yes, they add real members
  • No — they're resolved by the compiler at call sites
  • Only for sealed types
  • Only at runtime via reflection

Answer: No — they're resolved by the compiler at call sites. They don't change the type or add real members; the compiler resolves them at call sites, which is why you can extend types you don't own.

Can an extension method take extra parameters?

  • Yes, anything after the 'this' parameter is a normal argument
  • No, only the 'this' parameter is allowed
  • Only one extra parameter
  • Only optional parameters

Answer: Yes, anything after the 'this' parameter is a normal argument. Anything after the 'this' parameter is a normal argument, e.g. age.IsBetween(18, 65).

What must the method itself be (besides being in a static class)?

  • public
  • virtual
  • static
  • abstract

Answer: static. The method itself must also be static, not just the class (CS1106 otherwise).

If a type already has an instance method with the same name as an extension, which wins?

  • The extension method
  • The instance method
  • It's a compile error
  • Whichever is declared first

Answer: The instance method. Instance methods always win; the extension is only used when no matching instance method exists.

Why might an extension method not be found at the call site?

  • The type is sealed
  • The method isn't public
  • The class is generic
  • The namespace containing the static class isn't in scope (missing using)

Answer: The namespace containing the static class isn't in scope (missing using). To use an extension, the namespace of its static class must be in scope via a using directive — that's the usual 'method not found' cause.

How is LINQ (Where, Select, OrderBy) implemented?

  • As keywords built into the compiler
  • As extension methods on IEnumerable<T> in System.Linq
  • As instance methods on List<T>
  • As static methods on the Enumerable struct only

Answer: As extension methods on IEnumerable<T> in System.Linq. Where, Select, OrderBy and the rest are extension methods on IEnumerable<T> in System.Linq — that's why you add 'using System.Linq;'.

When should you NOT use an extension method?

  • When extending string
  • When the type is third-party
  • When you own the type — add a real instance method instead
  • When chaining with LINQ

Answer: When you own the type — add a real instance method instead. If you own the type, add a real method; extensions are for adding behaviour to types you can't change.

Continue this course

Frequently asked questions

Why does the first parameter use 'this'?

The 'this' modifier on the first parameter is what turns an ordinary static method into an extension method. It tells the compiler 'this method can be called as if it were an instance method of that parameter's type'. value.Method() is just sugar for Class.Method(value).

Do extension methods modify the original type?

No. They don't change the type's source code or add real members to it — they're resolved by the compiler at call sites. That's why you can extend types you don't own, like string, int, or classes from a NuGet package.

Where do extension methods have to live?

Inside a static, non-nested class, and the method itself must be static with 'this' on its first parameter. To use it, the namespace containing that static class must be in scope (via a using directive).

What happens if an extension method has the same name as a real method?

Instance methods always win. If the type already has a matching method, the compiler calls that and ignores your extension. Extension methods only fill gaps where no instance method matches.

Is LINQ built from extension methods?

Yes — Where, Select, OrderBy, Sum and the rest are all extension methods on IEnumerable<T> defined in System.Linq. That's why you add 'using System.Linq;' to unlock them, and why your custom extensions chain seamlessly alongside them.