C# 14 extension blocks
Source: Dev.to
Introduction
Learn about C# 14 extension blocks while targeting .NET Core 9. This can be done by adding the following to the project file.
What are extension methods?
C# extension methods let you add new methods to existing types without modifying their source code or creating subclasses. They’re defined as static methods in a static class, with the first parameter prefixed by this to indicate the type being extended. Once in scope, they look and feel like native instance methods, which makes code more readable and expressive.
Extension blocks benefits
Unlike the old syntax, extension blocks allow you to define extension properties, operators, and static members for a type.
Old syntax
public static class StringExtensions
{
public static string CapitalizeFirstLetter(this string sender)
=> string.IsNullOrEmpty(sender)
? sender
: $"{char.ToUpper(sender[0])}{sender[1..].ToLower()}";
}
New syntax
public static class StringExtensions
{
extension(string sender)
{
public string CapitalizeFirstLetter()
=> string.IsNullOrEmpty(sender)
? sender
: $"{char.ToUpper(sender[0])}{sender[1..].ToLower()}";
}
}
Usage
string firstName = "KAREN";
string lastName = "SMITH";
var fullName = $"{firstName.CapitalizeFirstLetter()} {lastName.CapitalizeFirstLetter()}";
The “receiver” (the instance you are extending) is defined once for the entire block, making the code cleaner and more organized.
Receiver example
public static class BoolExtensions
{
extension(bool value)
{
public string IsEmpty() =>
value switch
{
true => "Yes is empty",
_ => "No is not empty"
};
public string ToYesNo() =>
value switch
{
true => "Yes",
_ => "No"
};
}
}
Inside the block, members are declared like regular instance members; you don’t need to repeat the this parameter for every method.
Static operators (not supported prior to C# 14)
public static class EnumerableExtensions
{
// The generic T is defined at the block level
extension(IEnumerable first)
{
// Extension operator
// Note: At least one parameter must match the extended type (IEnumerable)
public static IEnumerable operator +(IEnumerable left, IEnumerable right)
=> left.Concat(right);
// Extension compound assignment (C# 14 feature)
// Allows `collection += item` behavior
public static IEnumerable operator +(IEnumerable left, T item)
=> left.Append(item);
}
}
Sample usage
var list1 = new[] { 1, 2 };
var list2 = new[] { 3, 4 };
var combined = list1 + list2; // Yields { 1, 2, 3, 4 }
Rules for extension operators
- Static context: Extension operators must be declared
public static, just like standard operators. - Identity rule: One of the operator’s parameters must be identity‑convertible to the type being extended (e.g., if you extend
T[], one parameter must beT[]). - Compound assignments: C# 14 introduces user‑defined compound assignment operators (such as
+=), which can now be implemented as extensions.
Constrained generic operators
You can combine extension operators with generic‑math constraints (e.g., INumber) to perform arithmetic on collections of numbers.
public static class NumericExtensions
{
extension(IEnumerable source) where T : INumber
{
public IEnumerable OnlyPositive => source.Where(x => x > T.Zero);
}
}
Article source code
Summary
Using extension blocks gives developers cleaner code, improving readability and maintain‑ability compared to pre‑C# 14 extension methods. When adopting extension blocks in a team, be sure to explain the feature to members who are accustomed to conventional extension methods.


