After coming up against this problem myself in trying to implement a generic Vector2
in C#, I\'ve done a bunch of investigation into this pr
Only you can tell if dynamic operator invocations will meet your performance requirements, but it's certainly possible to address some of your type-safety concerns with generics - there's no reason that everything has to be checked at run-time just because of one little dynamic call:
// Consider making this type immutable
public struct Vector2
{
public T X;
public T Y;
public Vector2(T x, T y)
{
this.X = x;
this.Y = y;
}
// The only dangerous operation on the type
public static Vector2 operator +(Vector2 a, Vector2 b)
{
return new Vector2((dynamic)a.X + b.X, (dynamic)a.Y + b.Y);
}
}
Now, the only dangerous operation is to actually add 2 vectors of the same type (the addition operator needs to work as expected on the type-argument), but everything else is perfectly type-safe, as it should be. You can't do new Vector
, add a Vector
and a Vector
, or assign the addition of two Vector
s to a Vector
. Note that none of these errors would have been caught at compile-time with your original solution.
Note that:
There's nothing to stop you from using generics here but going down the compiling-an-expression-tree route for the addition instead of dynamic
. Delegate invocations aren't free, but they should in theory be faster than the dynamic
approach in this case - at the very least, you avoid boxing value-types. Only you can tell if they will be fast enough, though.
In all cases, consider writing a static-constructor that validates that the type-argument in fact has a suitable addition operator, so that type-errors happen early on in the game.
EDIT (OP isn't satisfied with the performance of dynamic
here):
The expression-tree approach would look something like:
public struct Vector2
{
private static readonly Func Add;
// Create and cache adder delegate in the static constructor.
// Will throw a TypeInitializationException
// if you can't add Ts or if T + T != T
static Vector2()
{
var firstOperand = Expression.Parameter(typeof(T), "x");
var secondOperand = Expression.Parameter(typeof(T), "y");
var body = Expression.Add(firstOperand, secondOperand);
Add = Expression.Lambda>
(body, firstOperand, secondOperand).Compile();
}
public T X;
public T Y;
public Vector2(T x, T y)
{
this.X = x;
this.Y = y;
}
public static Vector2 operator +(Vector2 a, Vector2 b)
{
// Delegate invocation instead of dynamic operator invocation.
return new Vector2(Add(a.X, b.X), Add(a.Y, b.Y));
}
}