C#
extension methods
programming techniques
software development
coding practices

What is the best or most interesting use of Extension Methods you've seen?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Extension methods in C# let you add methods to existing types without modifying their source code or creating derived types. They are defined as static methods in static classes, with the first parameter prefixed by this. Extension methods power LINQ and are widely used for adding fluent APIs, null-safe operations, and domain-specific helpers to built-in types.

Syntax

csharp
1public static class StringExtensions
2{
3    public static bool IsNullOrEmpty(this string s)
4    {
5        return string.IsNullOrEmpty(s);
6    }
7}
8
9// Usage
10string name = "Alice";
11bool empty = name.IsNullOrEmpty();  // false

The compiler translates name.IsNullOrEmpty() into StringExtensions.IsNullOrEmpty(name).

Fluent String Operations

csharp
1public static class StringEx
2{
3    public static string Truncate(this string s, int maxLength, string suffix = "...")
4    {
5        if (string.IsNullOrEmpty(s) || s.Length <= maxLength)
6            return s;
7        return s[..(maxLength - suffix.Length)] + suffix;
8    }
9
10    public static string Repeat(this string s, int count)
11    {
12        return string.Concat(Enumerable.Repeat(s, count));
13    }
14
15    public static string ToSlug(this string s)
16    {
17        return Regex.Replace(s.ToLower().Trim(), @"[^a-z0-9]+", "-").Trim('-');
18    }
19}
20
21// Usage
22"Hello World, this is a long title".Truncate(20);     // "Hello World, this..."
23"ha".Repeat(3);                                         // "hahaha"
24"My Blog Post Title!".ToSlug();                         // "my-blog-post-title"

Null-Safe Extensions

Extension methods can be called on null objects — the this parameter receives null:

csharp
1public static class NullSafeExtensions
2{
3    public static string OrDefault(this string s, string defaultValue = "")
4    {
5        return string.IsNullOrWhiteSpace(s) ? defaultValue : s;
6    }
7
8    public static T OrDefault<T>(this T? obj, T defaultValue) where T : class
9    {
10        return obj ?? defaultValue;
11    }
12}
13
14string name = null;
15Console.WriteLine(name.OrDefault("Unknown"));  // "Unknown" — no NullReferenceException

This is useful because null.OrDefault() does not throw, unlike instance methods.

Collection Extensions

csharp
1public static class CollectionExtensions
2{
3    public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
4    {
5        return source == null || !source.Any();
6    }
7
8    public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class
9    {
10        return source.Where(x => x != null)!;
11    }
12
13    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
14    {
15        var batch = new List<T>(size);
16        foreach (var item in source)
17        {
18            batch.Add(item);
19            if (batch.Count == size)
20            {
21                yield return batch;
22                batch = new List<T>(size);
23            }
24        }
25        if (batch.Count > 0)
26            yield return batch;
27    }
28
29    public static string JoinWith<T>(this IEnumerable<T> source, string separator)
30    {
31        return string.Join(separator, source);
32    }
33}
34
35// Usage
36var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
37foreach (var batch in numbers.Batch(3))
38    Console.WriteLine(batch.JoinWith(", "));
39// 1, 2, 3
40// 4, 5, 6
41// 7

Dictionary Extensions

csharp
1public static class DictionaryExtensions
2{
3    public static TValue GetOrAdd<TKey, TValue>(
4        this Dictionary<TKey, TValue> dict,
5        TKey key,
6        Func<TValue> factory)
7    {
8        if (!dict.TryGetValue(key, out var value))
9        {
10            value = factory();
11            dict[key] = value;
12        }
13        return value;
14    }
15
16    public static TValue GetOrDefault<TKey, TValue>(
17        this IReadOnlyDictionary<TKey, TValue> dict,
18        TKey key,
19        TValue defaultValue = default)
20    {
21        return dict.TryGetValue(key, out var value) ? value : defaultValue;
22    }
23}
24
25// Usage
26var cache = new Dictionary<string, List<int>>();
27cache.GetOrAdd("users", () => new List<int>()).Add(42);

Enum Extensions

csharp
1public static class EnumExtensions
2{
3    public static string GetDescription(this Enum value)
4    {
5        var field = value.GetType().GetField(value.ToString());
6        var attr = field?.GetCustomAttribute<DescriptionAttribute>();
7        return attr?.Description ?? value.ToString();
8    }
9}
10
11public enum Status
12{
13    [Description("In Progress")]
14    InProgress,
15    [Description("Completed Successfully")]
16    Completed
17}
18
19Console.WriteLine(Status.InProgress.GetDescription());
20// "In Progress"

DateTime Extensions

csharp
1public static class DateTimeExtensions
2{
3    public static bool IsWeekend(this DateTime date)
4    {
5        return date.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;
6    }
7
8    public static DateTime StartOfWeek(this DateTime date, DayOfWeek startOfWeek = DayOfWeek.Monday)
9    {
10        int diff = (7 + (date.DayOfWeek - startOfWeek)) % 7;
11        return date.AddDays(-diff).Date;
12    }
13
14    public static string TimeAgo(this DateTime date)
15    {
16        var span = DateTime.UtcNow - date;
17        if (span.TotalMinutes < 1) return "just now";
18        if (span.TotalHours < 1) return $"{(int)span.TotalMinutes}m ago";
19        if (span.TotalDays < 1) return $"{(int)span.TotalHours}h ago";
20        if (span.TotalDays < 30) return $"{(int)span.TotalDays}d ago";
21        return date.ToString("MMM d, yyyy");
22    }
23}
24
25// Usage
26DateTime.Now.IsWeekend();                  // true/false
27DateTime.Now.StartOfWeek();                // Monday of this week
28DateTime.UtcNow.AddHours(-3).TimeAgo();   // "3h ago"

LINQ-Style Extension (How LINQ Works)

LINQ itself is built on extension methods:

csharp
1// Simplified version of how LINQ's Where works
2public static IEnumerable<T> MyWhere<T>(
3    this IEnumerable<T> source,
4    Func<T, bool> predicate)
5{
6    foreach (var item in source)
7    {
8        if (predicate(item))
9            yield return item;
10    }
11}
12
13var evens = new[] { 1, 2, 3, 4, 5 }.MyWhere(x => x % 2 == 0);
14// [2, 4]

Common Pitfalls

  • Extension methods on object: Extending object makes the method appear on every type, cluttering IntelliSense. Limit extensions to specific types or interfaces.
  • Namespace pollution: Extension methods are visible when their containing namespace is imported. Put extensions in a descriptive namespace (e.g., MyApp.Extensions) so they are opt-in, not globally polluting.
  • Confusion with instance methods: If a type later adds an instance method with the same signature, the instance method takes precedence and the extension is silently ignored. This can cause surprising behavior after a library update.
  • Extension methods on interfaces hiding implementation: Calling an extension on an interface always uses the extension, even if the implementing class has a method with the same name (unless the variable is typed to the class). This breaks polymorphism expectations.
  • Performance assumptions: Extension methods are just static method calls — there is no virtual dispatch overhead. However, LINQ-style extensions that return IEnumerable<T> with yield return create state machines. Be aware of deferred execution and multiple enumeration.

Summary

  • Extension methods add methods to existing types without modifying them
  • They enable fluent APIs, null-safe operations, and domain-specific helpers
  • LINQ is built entirely on extension methods — you can create your own LINQ-style operators
  • Null-safe extensions are powerful because null.ExtensionMethod() does not throw
  • Keep extensions in dedicated namespaces to avoid polluting IntelliSense
  • Instance methods take precedence over extensions with the same signature

Course illustration
Course illustration

All Rights Reserved.