在尝试在C#中实现通用Vector2<int/float/double>
后遇到这个问题后,我对这个问题进行了一系列调查,这个问题也有描述:
Less generic generics? A possible solution for arithmetic in C# generics
这些链接包含更多背景信息和迷人的解决方案方法:
https://jonskeet.uk/csharp/miscutil/usage/genericoperators.html
http://www.codeproject.com/KB/cs/genericnumerics.aspx
现在C#4.0已经推出了新的多功能dynamic
类型,我对这个辉煌的SO社区的问题是:它是一个可以用来构建高性能,通用Vector / Matrix /等的工具。数字类型?
显然,Vector2可以通过以下方式构建:
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);
}
}
但是这种方法我们在这里没有类型约束,所以你可以制作一个Vector2(3, 12.4572)
。有没有办法我们可以将动态成员与类型参数Vector2<int>
混合来执行我们的数学运算,就像使用int
s一样?
也许某种形式的铸造可以用来确保this.X
是一个T
,虽然我不知道它会如何表现。
只有你可以判断动态操作员调用是否符合你的性能要求,但是通过泛型来解决你的一些类型安全问题当然是可能的 - 没有理由因为一个小的动态调用而在运行时必须检查所有内容:
// Consider making this type immutable
public struct Vector2<T>
{
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<T> operator +(Vector2<T> a, Vector2<T> b)
{
return new Vector2<T>((dynamic)a.X + b.X, (dynamic)a.Y + b.Y);
}
}
现在,唯一危险的操作是实际添加2个相同类型的向量(加法运算符需要在类型参数上按预期工作),但其他一切都是完全类型安全的,应该是。你不能做new Vector<int>("a", 5)
,添加Vector<int>
和Vector<string>
,或指定添加两个Vector<int>
s到Vector<string>
。请注意,在原始解决方案的编译时,不会捕获这些错误。
注意:
dynamic
。委托调用不是免费的,但理论上它们应该比dynamic
方法更快 - 在这种情况下 - 至少,你避免拳击值类型。但是,只有你可以判断它们是否足够快。编辑(OP对dynamic
的性能不满意):
表达式树方法看起来像:
public struct Vector2<T>
{
private static readonly Func<T, T, T> 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<Func<T, T, T>>
(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<T> operator +(Vector2<T> a, Vector2<T> b)
{
// Delegate invocation instead of dynamic operator invocation.
return new Vector2<T>(Add(a.X, b.X), Add(a.Y, b.Y));
}
}