After coming up against this problem myself in trying to implement a generic Vector2<int/float/double>
in C#, I've done a bunch of investigation into this problem, also described in this question:
Less generic generics? A possible solution for arithmetic in C# generics
These links contain some more background information and fascinating solution approaches:
https://jonskeet.uk/csharp/miscutil/usage/genericoperators.html
http://www.codeproject.com/KB/cs/genericnumerics.aspx
Now that C# 4.0 is out with its new versatile dynamic
type, my question for the brilliant SO community, is this: is it a tool that could be used perhaps to build performant, generic Vector/Matrix/etc. numeric types?
Clearly a Vector2 could be built by simply like so:
public struct Vector2
{
public dynamic X;
public dynamic Y;
public Vector2(dynamic x, dynamic y)
{
this.X = x;
this.Y = y;
}
public static Vector2 operator+(Vector2 a, Vector2 b)
{
return new Vector2(a.X + b.X, a.Y + b.Y);
}
}
but with this approach we have no type constraint here, so you could make a Vector2(3, 12.4572)
. Is there a way that we could mix dynamic members with a type parameter Vector2<int>
to perform our math operations as would be done with int
s?
Perhaps some form of casting could be used to ensure this.X
is a T
, though I don't know how that would perform.
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:
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<int>("a", 5)
, add aVector<int>
and aVector<string>
, or assign the addition of twoVector<int>
s to aVector<string>
. 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 thedynamic
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: