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 convention 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 once for the entire block, making the code cleaner and more organized.
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.
Not supported prior to C# 14
Static operators can be used with C# 14, but not in preview.
public static class EnumerableExtensions
{
// The generic T is defined at the block level
extension<T>(IEnumerable<T> first)
{
// Extension Operator
// Note: At least one parameter must match the extended type (IEnumerable<T>)
public static IEnumerable<T> operator +(IEnumerable<T> left, IEnumerable<T> right)
=> left.Concat(right);
// Extension Compound Assignment (C# 14 feature)
// This allows 'collection += item' behavior
public static IEnumerable<T> operator +(IEnumerable<T> 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: Like standard operators, extension operators must be declared public static.
- 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 be T[]).
- Compound Assignments: C# 14 also introduces user-defined compound assignment operators (like +=), 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<T>(IEnumerable<T> source) where T : INumber<T>
{
public IEnumerable<T> OnlyPositive => source.Where(x => x > T.Zero);
}
}
Article source code
Summary
Using extension blocks means a developer has cleaner code, both for readability and maintenance, than before C#14. When using extension blocks in a team of developers, make sure to explain them to team members, especially developers who tend not to work with conventional extension methods.



Top comments (0)