 It's dangerous to code alone! Take this.

# Generic Math Part 3: Making an Interface with Operators

Published on 19 Oct 2022.

This is Part 3 in a series about generic math. You can view all parts here: Part 1 Part 2 Part 3 Part 4

This is the third post in a series about generic math. We’ve been working on a toy problem to make this `Add` method work with all sorts of different types:

``````public static T Add<T>(T a, T b, T c)  where T : IAddable<T>
{
return a + b + c;
}
``````

It is working as expected, and we made our `Point` class implement `IAddable<T>`, but the built-in numeric types like `float`, `double`, and `int` do not implement our `IAddable<T>` interface and cannot be modified to support it.

That’s the issue we’ll address here.

In these situations, one possibility is the Adapter Pattern. The Adapter Pattern is a tool that can be used anytime you have a square peg, but need a round hole. When an object has one interface but you need it to look like another and can’t (or don’t want to) change it directly.

It requires making an extra class and an extra object to adapt the desired interface to the interface presented by the object it is adapting.

So we could, for example, make this class:

``````public class AddableFloat : IAddable<AddableFloat>
{
public float Value { get; }
public AddableFloat(float value) { Value = value; }
{
}
}
``````

Now we can call our `Add` method with `AddableFloat` objects instead of `float`, though we’ll have to convert to `AddableFloat` before and back to `float` after:

``````float sum = Add(new AddableFloat(2), new AddableFloat(4), new AddableFloat(-2)).Value;
``````

The Adapter Pattern is a useful general-purpose pattern. If we had to, we could make this work. (And with some effort, we could make this `AddableFloat` type nicer.)

Fortunately, this is not the only way to solve this problem, and not the one we’ll spend the rest of our time pursuing. But it can be a useful tool in other similar situations.

## The BCL’s Generic Math Interfaces

The main problem we have is that we need to constrain our generic method to only working with types that implement a specific interface. But we can’t just make our own interface and then apply it to existing types like `float` and `int`. But what if those types already implemented an interface that included a definition for the `+` operator?

Turns out, there’s a whole pile of interfaces that do exactly this!

These interfaces all live in the `System.Numerics` namespace, and if you’re doing much with them, you’ll probably want to include this at the top of your files:

``````using System.Numerics;
``````

There is a giant pile of these interfaces, and we’ll talk about them in a bit more depth in the next post. But for now, there’s really only one that we need, and that is the interface `IAdditionOperators`, which is defined something like this:

``````public interface IAdditionOperators<TSelf, TOther, TResult> where TSelf : IAdditionOperators<TSelf, TOther, TResult>
{
static abstract TResult operator +(TSelf left, TOther right);
}
``````

They had the same issue we had when trying to ensure that an operator is only defined in a type that it is actually used in. That’s why they’ve got the `where TSelf : IAdditionOperators<TSelf, TOther, TResult>` at the end of the first line.

Our `IAddable<T>` interface assumed that both operands and the return type were all the same, while this version uses a different generic type parameter for each of those, which makes it possible to account for add operators that mix types. That comes at the cost of three generic type parameters. But, in theory, it means there’s no necessity to ever define your own interface that simply expects addition of any types, because `IAdditionOperators` should cover you.

All of the built-in number types implement this interface. For example, `int` implements `IAdditionOperators<int, int, int>`, meaning it has a defined operator for adding two `int` values to get a third `int` value. Meanwhile, `float` implements `IAdditionOperators<float, float, float>`.

Instead of defining our own interface, we could simply use `IAdditionOperators` and make our `Point` class also implement that interface.

Our `Point` class becomes this:

``````public class Point : IAdditionOperators<Point, Point, Point>
{
public float X { get; }
public float Y { get; }
public Point(float x, float y) { X = x; Y = y; }
public static Point operator +(Point a, Point b) =>
new Point(a.X + b.Y, a.Y + b.Y);
}
``````

The actual operator definition remains identical, but the interface `Point` implements changed.

One important question, at this point, is whether it is better to make your own operator interfaces or use the ones in `System.Numerics`. This feature is new enough that I’m not confident in anybody’s claims of “best practices” just yet.

I do think there’s likely a time and place for each. Using the ones in `System.Numerics` means you don’t need to actually define an interface. And it means any method in the Base Class Library that expects one of those interfaces will be able to work for your type as well, as long as your type implements that interface. On the other hand, `IAdditionOperators<Point, Point, Point>` is more cumbersome than `IAddable<T>`. I think either pathway could be reasonable, depending on what, exactly, you’re trying to do, though I think I’d probably have a bias toward the ones in `System.Numerics`, if there isn’t a compelling reason to do otherwise.

Lastly, our `Add` method needs an update:

``````public static T Add<T>(T a, T b, T c) where T : IAdditionOperators<T, T, T>
{
return a + b + c;
}
``````

Since `Point` now implements `IAdditionOperators<Point, Point, Point>`, we can call this with:

``````Point sum = Add<Point>(new Point(2, 1), new Point(1, 2), new Point(-2, -2));
``````

Since `float` implements `IAdditionOperators<float, float, float>` and `int` implements `IAdditionOperators<int, int, int>`, both of these work:

``````int intSum = Add<int>(1, 2, 3);
float floatSum = Add<float>(4f, 5f, 6.7f);
``````

There are a lot of interfaces in `System.Numerics` beyond just `IAdditionOperators`. We’ll talk about what’s there in a bit more depth next time.

This is Part 3 in a series about generic math. You can view all parts here: Part 1 Part 2 Part 3 Part 4