Extension Members with .NET 10 / C# 14

Programming .NET C#

The Evolution of Extension Methods

When Microsoft introduced extension methods in C# 3.0 alongside LINQ, they changed the way developers could design APIs. Suddenly, we could “add” methods to existing types without modifying their source code or creating wrapper classes. It became a cornerstone feature for building expressive libraries and fluent APIs.

Fast forward to .NET 10 and C# 14, and the concept has evolved. The language now introduces Extension Members, a powerful enhancement that expands what extension methods can do while also improving developer ergonomics.

In this article we’ll explore:

  1. The original concept of extension methods
  2. How to implement them for your own types
  3. The limitations of the classic approach
  4. The new extension member syntax in C# 14
  5. A practical example evolving from extension methods to extension members

Let’s dive in.

The Original Concept: Extension Methods

Extension methods allow you to add methods to an existing type without modifying its source code. This is especially useful when:

  • You don’t own the type (e.g., framework types)
  • You want to keep utility behavior separate
  • You want to build fluent APIs

Technically, extension methods are static methods defined in a static class, but they are invoked as if they were instance methods on the target type.

This is done using the this keyword on the first parameter of the method.

Example: A Simple String Extension

public static class StringExtensions
{
    public static bool IsLongerThan(this string value, int length)
    {
        return value.Length > length;
    }
}

Usage:

string text = "Hello World";
bool result = text.IsLongerThan(5);

Why Extension Methods Are So Powerful

Extension methods became extremely popular because they enable:

1. Fluent APIs

LINQ is the most famous example.

var evenNumbers = numbers
    .Where(n => n % 2 == 0)
    .OrderBy(n => n)
    .ToList();

All these methods (Where, OrderBy, etc.) are extension methods on IEnumerable<T>.

2. Enhancing Framework Types

You can extend types like:

  • string
  • DateTime
  • IEnumerable<T>
  • HttpClient
  • Any third-party type

3. Cleaner Code

Instead of writing utility helpers like:

StringHelpers.IsLongerThan(text, 5);

You can write:

text.IsLongerThan(5);

Which reads much more naturally.

 

Creating Extension Methods for Our Own Types

Let’s build a slightly more realistic example.

Imagine we have a simple domain object:

public class Order
{
    public string Status { get; set; }
    public decimal TotalAmount { get; set; }
}

Now suppose we want helper functionality such as:

  • Checking if the order is large
  • Marking it as shipped
  • Printing a formatted summary

Instead of bloating the Order class with helper logic, we can write extension methods.

public static class OrderExtensions
{
    public static bool IsLargeOrder(this Order order)
    {
        return order.TotalAmount > 1000;
    }

    public static void MarkAsShipped(this Order order)
    {
        order.Status = "Shipped";
    }

    public static string Summary(this Order order)
    {
        return $"Status: {order.Status}, Total: {order.TotalAmount:C}";
    }
}

Usage:

var order = new Order
{
    Status = "Processing",
    TotalAmount = 1500
};

if (order.IsLargeOrder())
{
    Console.WriteLine("High value order!");
}

order.MarkAsShipped();

Console.WriteLine(order.Summary());

Clean, readable, and nicely separated from the domain model. Remember: all of this was possible since .NET 3 Core.

The Slightly Annoying Part of Extension Methods

While extension methods are powerful, there’s one thing that becomes obvious when writing many of them.

Every method must repeat the receiver parameter:

this Order order

Again and again and again...   :-(

public static bool IsLargeOrder(this Order order)
public static void MarkAsShipped(this Order order)
public static string Summary(this Order order)

When you build larger extension libraries, this repetition becomes noticeable.

Imagine having 20 extension methods for the same type.

You end up repeating the same phrase over and over again.

From a developer experience perspective, that’s simply boilerplate.

And there was another kind of limitation.

The Biggest Limitation: Methods Only

Classic extension methods only allow methods.

You cannot add:

  • properties
  • operators
  • static members

For example, this would not work:

public bool IsLargeOrder => TotalAmount > 1000;

There was simply no syntax for extension properties.

This limitation became more noticeable as developers wanted more expressive APIs.

That’s where C# 14’s extension members come in.

Enter C# 14: Extension Members

With .NET 10 and C# 14, Microsoft introduced extension members, which evolve extension methods into a broader and more expressive system.

Extension members introduce a new syntax using the extension keyword.

Instead of repeating this Type parameter on every method, you declare an extension block.

Inside that block you can define:

  • extension methods
  • extension properties
  • extension operators
  • static extension members

This new structure lets you group related functionality and significantly reduce repetition.

Rewriting Our Example with Extension Members

Let’s revisit the Order example.

Old Style

 

public static class OrderExtensions
{
    public static bool IsLargeOrder(this Order order)
    {
        return order.TotalAmount > 1000;
    }

    public static void MarkAsShipped(this Order order)
    {
        order.Status = "Shipped";
    }
}

New Style (C# 14)

public static class OrderExtensions
{
    extension (Order order)
    {
        public bool IsLargeOrder()
        {
            return order.TotalAmount > 1000;
        }

        public void MarkAsShipped()
        {
            order.Status = "Shipped";
        }
    }
}

Notice the difference.

We declare the receiver once:

extension (Order order)

Every member inside automatically applies to that type.

No more repeating:

this Order order
this Order order
this Order order

Adding Extension Properties (New!)

Now let’s add something we couldn’t do before: an extension property.

public static class OrderExtensions
{
    extension (Order order)
    {
        public bool IsLargeOrder => order.TotalAmount > 1000;

        public string DisplayStatus =>
            $"Order Status: {order.Status}";
    }
}

Usage:

var order = new Order
{
    Status = "Processing",
    TotalAmount = 1500
};

Console.WriteLine(order.IsLargeOrder);
Console.WriteLine(order.DisplayStatus);

Now the API reads like a natural property of the type.

That was impossible with classic extension methods.

Extension Members Feel Like “Partial Types”

Conceptually, extension members feel similar to partial classes, except you don’t need access to the original source code.

You are essentially saying:

"For this type, here are some additional members."

This works particularly well for:

  • Domain logic
  • Library utilities
  • Clean architecture layers

You can keep your core domain models small while organizing behavior in separate extension blocks.

Grouping Related Behavior

Extension members also encourage better organization.

Imagine we want to group reporting logic for orders:

public static class OrderReportingExtensions
{
    extension (Order order)
    {
        public string Summary =>
            $"Status: {order.Status}, Total: {order.TotalAmount:C}";

        public bool RequiresManagerApproval =>
            order.TotalAmount > 5000;
    }
}

Now our code can read like this:

if (order.RequiresManagerApproval)
{
    Console.WriteLine(order.Summary);
}

That’s extremely expressive.

Static Extension Members

Another powerful capability is extending the type itself, not just instances.

For example:

public static class OrderFactoryExtensions
{
    extension (Order)
    {
        public static Order CreatePriority(decimal amount)
        {
            return new Order
            {
                Status = "Priority",
                TotalAmount = amount
            };
        }
    }
}

Usage:

var order = Order.CreatePriority(2500);

Real-World Use Cases

Extension members open interesting possibilities.

1. Domain Logic Separation

Instead of stuffing logic into entities:

Order.cs

You can organize logic by concern:

OrderExtensions.Validation.cs
OrderExtensions.Reporting.cs
OrderExtensions.BusinessRules.cs

2. Cleaner Framework Extensions

You can now write things like:

extension(HttpStatusCode status)
{
    public bool IsSuccess => (int)status >= 200 && (int)status < 300;
}

Usage:

if (response.StatusCode.IsSuccess)
{
    ...
}

which is cleaner than

status.IsSuccess()

3. Building Rich APIs

Libraries can expose APIs that feel native to the types they extend.

For example:

file.SizeInMB
file.IsLarge
file.ReadPreview()

Even if the type originally had none of those!

Compatibility with Existing Code

One important point: existing extension methods still work.

The new syntax is simply an alternative way of defining them.

That means:

  • No breaking changes
  • Existing libraries remain valid
  • Developers can migrate gradually

The compiler treats extension members in a similar way internally, preserving compatibility with the large ecosystem built on extension methods.

When Should You Use Extension Members?

They are particularly useful when:

You want properties

order.IsLargeOrder

instead of

order.IsLargeOrder()

You want cleaner grouping

Instead of repeating the receiver parameter.

You are building reusable libraries

Extension members allow much richer APIs.

You want better code organization

Separate extensions by feature or module.

Conclusion

Extension methods have been one of the most impactful features in the C# ecosystem. They enabled LINQ, fluent APIs, and countless library patterns.

With C# 14 and .NET 10, Microsoft takes the next logical step with Extension Members.

The improvements may seem subtle at first, but they provide meaningful benefits:

  • Less boilerplate
  • Better grouping of extensions
  • Support for properties and operators
  • Static extensions for types
  • More expressive APIs

Most importantly, the feature builds on the existing extension method model instead of replacing it. Your old code still works exactly the same, while new projects can take advantage of the improved syntax and capabilities.

If extension methods were about adding behavior, extension members are about designing better APIs.

And for library authors and application developers alike, that’s a powerful tool to have in the language.