Generic Math and Static Abstract Members in C# 11
C# 11 introduced static abstract members in interfaces, a powerful language capability that serves as the foundation for generic math support in .NET. This article explores how static abstract interface members work, how they solve long-standing limitations regarding operators in generics, and how the standard generic math interfaces allow developers to write algorithms that function across multiple numeric types without code duplication.
Static Abstract Interface Members
Prior to C# 11, interface members were inherently tied to object instances. While C# 8 introduced static methods with default implementations, interfaces could not declare abstract static members that implementing types were forced to define.
C# 11 allows the static and abstract
modifiers to be combined within an interface declaration. This requires
any implementing class or struct to provide an explicit static
implementation of that member:
public interface IFactory<TSelf> where TSelf : IFactory<TSelf>
{
static abstract TSelf Create();
}Because operators in C# are declared as public static
methods (such as
public static T operator +(T left, T right)), static
abstract members make it possible to define operators directly within
interfaces. This allows operators to be invoked polymorphically via
generic type parameters.
The Problem Generic Math Solves
Historically, writing a generic method to perform arithmetic was impossible in C#. A method such as the following would fail to compile:
// Did not compile prior to C# 11
T Add<T>(T a, T b)
{
return a + b; // Error: Operator '+' cannot be applied to operands of type 'T' and 'T'
}Because the compiler could not guarantee that an arbitrary type
T implemented the + operator, developers were
forced to duplicate algorithms for int, float,
double, and decimal, or resort to inefficient
reflection or expression compilation.
Generic Math Support
Generic math builds on static abstract interface members by
introducing a rich hierarchy of numeric interfaces in the
System.Numerics namespace. At the core of this system are
interfaces such as:
IAdditionOperators<TSelf, TOther, TResult>: Defines the addition operator (+).IComparisonOperators<TSelf, TOther, TResult>: Defines comparison operators (<,>,<=,>=).INumber<TSelf>: A comprehensive interface implemented by all standard numeric types, combining arithmetic, comparison, and formatting contracts.
These interfaces utilize the Curiously Recurring Template Pattern
(CRTP), where the implementing type passes itself as a generic type
parameter (e.g., int : INumber<int>). This guarantees
that operations return the correct concrete type rather than an
interface reference.
With these interfaces, generic arithmetic functions can be written cleanly:
using System.Numerics;
public static T SumCollection<T>(IEnumerable<T> values) where T : INumber<T>
{
T total = T.Zero;
foreach (var value in values)
{
total += value;
}
return total;
}This single method works seamlessly with int,
double, decimal, BigInteger, or
any custom type that implements INumber<T>.
Key Benefits
- Code Reusability: Mathematical algorithms, statistical calculations, and graphic operations only need to be written once rather than reimplemented for each numeric primitive.
- Performance: Because generic type constraints are resolved at compile time and JIT-compiled for value types, using static abstract members incurs zero runtime overhead or boxing penalties.
- Extensibility: Custom types, such as 2D vectors, fractional numbers, or complex numbers, can implement the standard numeric interfaces and instantly become compatible with existing generic algorithms across the .NET ecosystem.