C#
extension methods
static class
programming
.NET

Extension methods must be defined in a non-generic static class

Master System Design with Codemia

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

Introduction

In C#, an extension method is just a static method with special compiler support that lets you call it like an instance method. One of the language rules behind that feature is that the containing type must be a static, non-generic class, which is why the compiler rejects extension methods placed inside a generic container type.

The Valid Shape Of An Extension Method

A normal extension method looks like this:

csharp
1public static class StringExtensions
2{
3    public static bool IsNullOrWhite(this string? value)
4    {
5        return string.IsNullOrWhiteSpace(value);
6    }
7}

The rules are:

  • the method itself must be static
  • the first parameter uses the this modifier
  • the containing class must be static
  • the containing class must not be generic

Once compiled, the call:

csharp
bool result = "abc".IsNullOrWhite();

is really resolved as a static method call by the compiler.

Why A Generic Container Class Is Rejected

This is invalid:

csharp
1public static class Extensions<T>
2{
3    public static string Describe(this T value)
4    {
5        return value?.ToString() ?? "null";
6    }
7}

The compiler rejects it because extension-method discovery works through non-generic static classes imported into scope. Allowing generic containing types would complicate lookup and overload resolution in a way the language deliberately avoids.

In other words, the rule is not arbitrary. It keeps extension method binding predictable.

Generic Extension Methods Are Still Allowed

The restriction is on the containing class, not on the method's own type parameters. You can absolutely write a generic extension method inside a non-generic static class.

csharp
1public static class EnumerableExtensions
2{
3    public static bool IsEmpty<T>(this IEnumerable<T> source)
4    {
5        using var enumerator = source.GetEnumerator();
6        return !enumerator.MoveNext();
7    }
8}

That is the standard pattern when the extension itself needs to work across many types.

Generic Receiver Types Also Work

The type being extended can itself be generic.

csharp
1public static class DictionaryExtensions
2{
3    public static TValue GetOrDefault<TKey, TValue>(
4        this IDictionary<TKey, TValue> dictionary,
5        TKey key,
6        TValue defaultValue)
7    {
8        return dictionary.TryGetValue(key, out var value) ? value : defaultValue;
9    }
10}

Again, the class stays non-generic, but the method may use generic parameters freely.

Why The Class Is Static Too

The class cannot be instantiated because extension methods are not true object members. They are compile-time syntax sugar over static methods. Making the container static reinforces that the type is only a namespace-like holder for helper methods.

That also matches how you use them: by importing the namespace, not by creating an instance of the container class.

How The Compiler Finds Extension Methods

When the compiler sees value.SomeExtension(), it first looks for a real instance method. If none exists, it searches imported namespaces for eligible extension methods in non-generic static classes.

That search model would be much harder to reason about if the container class itself required type inference or separate generic construction.

Common Pitfalls

The most common mistake is thinking that because an extension method may itself be generic, the containing class can also be generic. It cannot. Another is forgetting that extension methods are only considered when the namespace containing the static class is in scope. Developers also sometimes try to use extension methods to simulate inheritance or override instance methods, but real instance methods always win in overload resolution.

Summary

  • Extension methods must live in a static, non-generic class.
  • The method can still be generic even though the containing class cannot.
  • The extended type may also be generic.
  • The rule exists to keep compiler lookup and overload resolution predictable.
  • If you see this compiler error, move the method into a non-generic static container class.

Course illustration
Course illustration

All Rights Reserved.