在尝试实现通用 Vector2<int/float/double>
时,我自己遇到了这个问题。在 C# 中,我对这个问题做了很多调查,也在这个问题中进行了描述:
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
type,我对杰出的 SO 社区的问题是:它是一个可以用来构建高性能、通用向量/矩阵等的工具吗?数字类型?
显然,可以像这样简单地构建 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
那样是吗?
也许可以使用某种形式的类型转换来确保 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>
相加发送至 Vector<string>
。请注意,使用原始解决方案在编译时不会捕获这些错误。
请注意:
没有什么可以阻止您在这里使用泛型但是沿着编译表达式树路线进行添加而不是
dynamic
。委托(delegate)调用不是免费的,但理论上它们应该比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));
}
}
关于generics - C# 4.0动态: A potential performant solution to numeric generics?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5013883/