Skip to content

Build1 publisher2 min readPublished

Summing an array of T in C# takes two interface constraints and a self-referring type parameter

Generic Math shipped in .NET 7 on 8 November 2022 so an interface could declare a static operator. Teams get compile-time arithmetic without reflection or dynamic, and pay in the constraint lists their APIs publish.

The Engineer · Build desk

What happened

  • Before the feature existed, a generic sum over an array of T failed to compile with CS0019. An overloaded operator is a static member of a concrete type, and a bare type parameter says almost nothing.
  • The intuitive fix of writing a constraint that names the plus symbol failed too, with CS1031, because constraints accept types and C# had no syntax for the presence of an operator.
  • C# answered with static abstract interface members, letting an interface declare a static operator and a static Zero property that a struct then implements.
  • A poll the guide's author ran in his own Telegram channel found that most developers did not use Generic Math and did not understand what the feature was for.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • capability Code that used reflection or dynamic to add two values of an unknown type can now state the requirement as a constraint the compiler checks. The guide extends that to parsing, generated database models and DDD types.
  • constraint Because the contracts use CRTP, a type opts in by naming itself in its own interface list, so retrofitting an existing domain type means editing its declaration.
  • decision Library authors have to decide what to expose: the two-interface constraint list in a public signature, or a concrete-typed API with the generic layer kept internal.
  • contradiction The objection in the proposal thread was that nobody could see a production use case, and the guide's six non-numeric applications are the answer to it. The argument is about where the feature belongs.

The sum that finally compiles constrains T twice. The guide's version requires `IAdditiveIdentity<T, T>` and `IAdditionOperators<T, T, T>`, and seeds the accumulator with `T.AdditiveIdentity` [9]. Two contracts for one loop, because the identity and the operator live in separate interfaces [10][12]. The System.Numerics contracts are deliberately fine-grained so you can assemble a derived abstraction out of small pieces [11].

Granularity is visible in the signatures. `IAdditionOperators` carries three type parameters, TSelf, TOther and TResult, so addition is not assumed to be homogeneous; adding two Ts and getting a T back is the case where you write the same name three times [13]. The interface also constrains its own implementer, `where TSelf : IAdditionOperators<TSelf, ...>` [13]. The curiously recurring template pattern shows up here, and the guide says the feature and the framework interfaces both use it heavily [10].

Kirill Maurin pointed out in 2020 that C# could not express even the simplest generic summation [17]. Miguel de Icaza and Aaron Bockover presented the first working draft to Microsoft's Language Design Meeting on 29 June 2020, which the guide describes as the result of a year's work [6]. Counting from the start of that year, roughly 40 months passed before the shipped release [19]. In the csharplang discussion, issue 4436, reception was generally positive, though some commenters feared C++-style complexity and others could not see a production use case [7].

The application list is where the feature stops being about numbers. The guide applies the same mechanics to parsing, generated database models, DDD types, architectural boundaries, tensors and SIMD [16]. The substitution in each case is a compile-time contract in place of reflection, dynamic, or a runtime object hierarchy [1]. For that to pay in your code, the generic paths have to run over types that implement the contracts; the built-in ones do, since following `int` upward through its interfaces reaches INumberBase [12].

The guide asserts the adoption cost without measuring it. It covers limitations and alternative designs on the grounds that static abstract constraints become expensive when used casually [16]. The complete interface hierarchy is a UML diagram too large to display well in the article, so the author posted the svg to his Telegram channel instead [14].

In my view the trade-off is easy where a parser or a generated model would otherwise reach for dynamic, and hard for a domain type that only ever adds to itself. There you pay two interface constraints and a self-referring type parameter for a contract only your own code reads.

What to watch

  • Whether the guide's limitations section attaches a measured build-time or binary-size figure to static abstract constraints.
  • Whether library authors publish System.Numerics constraint lists in public signatures or keep the generic layer internal behind concrete types.
  • A poll wider than one Telegram channel would show whether non-use of Generic Math is general or specific to that audience.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories