Codectionary / Developer documentation / C#

Extension Methods

Extension methods let you add new methods to an existing type - including types you don't own, like built-in .NET types or third-party classes - without modifying its source code or creating a subclass. They're defined as static methods in a static class, with the first parameter prefixed with this to indicate which type they extend. LINQ's Where(), Select(), and friends are themselves implemented as extension methods on IEnumerable<T>.

Syntax

public static returnType MethodName(this Type target, ...) { }

Examples

Basic Extension Method

Adding a new method to the built-in string type.

public static class StringExtensions
{
    public static bool IsPalindrome(this string text)
    {
        string cleaned = text.ToLower().Replace(" ", "");
        return cleaned == new string(cleaned.Reverse().ToArray());
    }
}

string word = "racecar";
Console.WriteLine(word.IsPalindrome());  // true - called just like a regular method

Extension Methods on Collections

A very common use case, similar to how LINQ itself extends IEnumerable<T>.

public static class ListExtensions
{
    public static double Median(this List<int> numbers)
    {
        var sorted = numbers.OrderBy(n => n).ToList();
        int mid = sorted.Count / 2;
        return sorted.Count % 2 == 0
            ? (sorted[mid - 1] + sorted[mid]) / 2.0
            : sorted[mid];
    }
}

List<int> data = new List<int> { 5, 1, 4, 2, 3 };
Console.WriteLine(data.Median());  // 3

Chaining Extension Methods

Extension methods work seamlessly alongside built-in and LINQ methods in a fluent chain.

public static class IntExtensions
{
    public static bool IsPrime(this int n)
    {
        if (n < 2) return false;
        for (int i = 2; i * i <= n; i++)
            if (n % i == 0) return false;
        return true;
    }
}

List<int> numbers = Enumerable.Range(1, 20).ToList();
var primes = numbers.Where(n => n.IsPrime()).ToList();
Console.WriteLine(string.Join(", ", primes));  // 2, 3, 5, 7, 11, 13, 17, 19

Best practices

  • Use extension methods to add genuinely reusable helper functionality to a type, especially one you don't own and can't otherwise modify
  • Put extension methods in a well-named static class (StringExtensions, DateTimeExtensions) so they're easy to find and organize
  • Don't overuse extension methods for logic that would fit more naturally as a regular method on a type you do control - reserve them for genuinely external types or cross-cutting helpers
  • Remember an extension method never overrides a real instance method with the same signature - a genuine instance method always takes priority if one exists

At a glance

Purpose
Applications on the .NET platform
File extension
.cs
Runs in
.NET runtime
Usually used with
.NET SDK and libraries

Specifications & further reading

Related C# documentation

Delegates
A delegate is a type-safe reference to a method - essentially a variable that can hold a method and be invoked like one. Delegates enable passing behavior around as data, forming the foundation for events, callbacks, and LINQ. C# provides built-in generic delegate types, Func<> and Action<>, that cover the vast majority of use cases without needing to declare a custom delegate type.
Events
An event is a special kind of delegate field that provides a publish-subscribe pattern: the declaring class can raise (invoke) the event, but external code can only subscribe (+=) or unsubscribe (-=) - it cannot invoke the event directly or overwrite existing subscribers. This makes events the standard, safe way to notify other parts of a program that something has happened, like a button being clicked or a value changing.
Nullable Reference Types & Null-Coalescing
Since C# 8, nullable reference types let the compiler track and warn about potential null reference issues at compile time, even for reference types like string and custom classes (which were always implicitly nullable before). A type like string means 'never null' under this feature, while string? explicitly allows null. The null-coalescing operator (??) and null-conditional operator (?.) provide concise syntax for safely working with potentially-null values.
Pattern Matching
Pattern matching, expanded significantly since C# 7, lets you test a value against a shape or condition using is, switch expressions, and more, often extracting matched data in the same step. It covers type patterns (checking and casting in one step), property patterns (matching on an object's properties), and relational patterns (matching numeric ranges), making conditional logic more expressive and less error-prone than manual casting.