能否通过泛型约束(when)实现泛型类中各类运算符的合法调用?
when) to Make Operator Code Legal? Great question! Let's break this down clearly—C# doesn't let you directly use operators like < (or any of the operators you listed: +, -, !, ~, ++, --, *, /, %, &, |, ^, <<, >>, ==, !=, <, >, <=, >=) on a generic type T just with a basic when constraint out of the box. But there are several proven patterns to make this work, tailored to your .NET version and requirements.
Key Limitation Up Front
The core issue is that prior to .NET 7, there were no built-in interfaces that represent operator support (like "this type can be compared with <" or "this type can be added"). The when keyword only lets you constrain T to types that implement specific interfaces, inherit from a base class, or meet other narrow criteria—none of which directly map to operator support.
Recommended Solutions
1. .NET 7+ Use Generic Math (Best Option)
If you're targeting .NET 7 or newer, Microsoft introduced Generic Math interfaces (like INumber<T>, IComparable<T>, IAdditionOperators<T,T,T>, etc.) that explicitly model operator support. This lets you use operators directly in generic code with full compile-time safety.
using System.Numerics; public class MyClass<T> where T : INumber<T> { public void MyMethod(T a, T b) { // All these operators work directly! bool isLessThan = a < b; T sum = a + b; bool areEqual = a == b; T product = a * b; bool isNotEqual = a != b; // ... and all other operators you listed } }
Pros: Type-safe, performant, and the most idiomatic modern approach. Covers all numeric operators and comparisons.
Cons: Requires .NET 7 or later.
2. Use Interface Constraints + Explicit Method Calls (Pre-.NET 7)
For comparison operators (like <, >, <=, >=), you can constrain T to IComparable<T> and use the CompareTo method instead of direct operators. For arithmetic operators, you'd need custom interfaces (or use the next pattern).
public class MyClass<T> where T : IComparable<T> { public void MyMethod(T a, T b) { // Replace a < b with CompareTo check bool isLessThan = a.CompareTo(b) < 0; bool isGreaterThan = a.CompareTo(b) > 0; bool areEqual = a.CompareTo(b) == 0; // ... } }
Pros: Compile-time type safety, works on all .NET versions.
Cons: Doesn't support arithmetic operators out of the box—you'd have to define your own interfaces (e.g., IAddable<T>) and implement them for types you want to use.
3. Pass Operator Delegates (Flexible for All Operators)
If you need to support any operator (including arithmetic) on pre-.NET 7 versions, you can pass delegates that encapsulate the operator logic when instantiating your generic class. This gives you full flexibility for any operator.
public class MyClass<T> { // Store delegates for the operators you need private readonly Func<T, T, bool> _lessThanOperator; private readonly Func<T, T, T> _additionOperator; private readonly Func<T, T, bool> _equalityOperator; public MyClass( Func<T, T, bool> lessThanOperator, Func<T, T, T> additionOperator, Func<T, T, bool> equalityOperator) { _lessThanOperator = lessThanOperator ?? throw new ArgumentNullException(nameof(lessThanOperator)); _additionOperator = additionOperator ?? throw new ArgumentNullException(nameof(additionOperator)); _equalityOperator = equalityOperator ?? throw new ArgumentNullException(nameof(equalityOperator)); } public void MyMethod(T a, T b) { bool isLessThan = _lessThanOperator(a, b); T sum = _additionOperator(a, b); bool areEqual = _equalityOperator(a, b); // ... } } // Usage example var intClass = new MyClass<int>( (x, y) => x < y, (x, y) => x + y, (x, y) => x == y); intClass.MyMethod(3, 5);
Pros: Supports every operator you listed, works on all .NET versions.
Cons: Requires passing delegates at instantiation, which adds boilerplate.
4. Use dynamic (Quick but Unsafe)
As a last resort, you can cast T to dynamic to bypass compile-time checks. This lets you use operators directly, but you lose type safety—you'll only get errors at runtime if T doesn't support the operator.
public class MyClass<T> { public void MyMethod(T a, T b) { // Runtime check only—no compile-time safety bool isLessThan = (dynamic)a < (dynamic)b; T sum = (dynamic)a + (dynamic)b; // ... } }
Pros: No boilerplate, works with any type that supports the operator.
Cons: No compile-time validation, potential runtime exceptions, and slightly worse performance due to dynamic dispatch.
Summary
- If you can use .NET 7+: Go with Generic Math interfaces (
INumber<T>etc.)—it's the cleanest, safest solution. - Pre-.NET 7 for comparisons: Use
IComparable<T>andCompareTo. - Pre-.NET 7 for all operators: Use delegate injection for full flexibility.
- Avoid
dynamicunless you're okay with runtime risks.
内容的提问来源于stack exchange,提问作者Christophe Debove

