Vit*_*meo 12 .net c# c++ generics templates
我希望每种基本类型都有2d矢量类.
现在,为了确保最佳的运行时性能并能够使用许多实用程序函数,我需要为每个基元(Vector2Int,Vector2Float,Vector2Long等)提供单独的类.
这只是很多复制粘贴,如果我必须做出改变,我必须记住在每个类和每个实用功能中都要做.
有什么东西可以让我写一些像C++模板(或者有什么方法可以创建它)?
我创建了一个小概念来向您展示这将如何工作:
// compile is a keyword I just invented for compile-time generics/templates
class Vector2<T> compile T : int, float, double, long, string
{
public T X { get; set; }
public T Y { get; set; }
public T GetLength()
{
return Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2));
}
}
// during compilation, code will be automatically generated
// as if someone manually replaced T with the types specified after "compile T : "
/*
VALID EXAMPLE (no compilation errors):
autogenerated class Vector2<int>
{
public int X { get; set; }
public int Y { get; set; }
public int GetLength()
{
return Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2));
}
}
UNVALID EXAMPLE (build failed, compilation errors):
autogenerated class Vector2<string>
{
public string { get; set; } // ok
public string { get; set; } // ok
public string GetLength()
{
return Math.Sqrt(Math.Pow(X, 2) + Math.Pow(Y, 2)); // error! string cannot be used with Math.Pow()
// and Math.Sqrt doesn't accept string type
}
}
*/
Run Code Online (Sandbox Code Playgroud)
是否有一些聪明的方法来实现这一点,或者这完全不可能?
很抱歉不太清楚,但让我解释一下问题所在.
考虑使用普通的C#泛型.GetLength()方法不会编译,因为我想要使用的所有类型(int,float,double,long)都需要共享Math.Pow()应该接受的接口作为参数.
字面上用类型名称替换"T"标记可以解决这个问题,提高灵活性,达到手写代码性能并加快开发速度.
我创建了自己的模板生成器,通过编写C#代码生成C#代码:) http://www.youtube.com/watch?v=Uz868MuVvTY
不幸的是,C#中的泛型与C++中的模板非常不同.为了实现这一点,对于不同的类型,IArithmetic必须存在共享接口(例如)(已经被高度请求但未实现)*,并且现在不在框架中.
这可以通过代码生成和T4模板来完成,但是它需要基于共享的"模板"为每种类型生成代码.
*注意:连接请求似乎被阻止,至少是暂时的.
这个问题的两个解决方案:
制作一个抽象类或接口计算器[t]并为您关心的类型实现它.将计算器的实例传递给向量类,以便它们可以使用它来进行数学运算.
使用表达式树,实际上就可以创建一个静态类计算器[T]有像加,战俘等在静态构造方法,你可以编译动态表情和具有静态方法调用这些编译lambda表达式.使用这种方法,您不必为每种类型实现计算器或传递它(因为它的静态).
例如:
public static class Calculator<T> {
public static readonly Func<T, T, T> Add;
public static readonly Func<T, T, T> Pow;
static Calculator() {
var p1 = Expression.Parameter(typeof(T));
var p2 = Expression.Parameter(typeof(T));
var addLambda = Expression.Lambda<Func<T, T, T>>(Expression.Add(p1, p2), p1, p2);
Add = addLambda.Compile();
// looks like the only Pow method on Math works for doubles
var powMethod = typeof(Math).GetMethod("Pow", BindingFlags.Static | BindingFlags.Public);
var powLambda = Expression.Lambda<Func<T, T, T>>(
Expression.Convert(
Expression.Call(
powMethod,
Expression.Convert(p1, typeof(double)),
Expression.Convert(p2, typeof(double)),
),
typeof(T)
),
p1,
p2
);
Pow = powLambda.Compile();
}
}
// and then in your class
T a, b;
var sum = Calculator<T>.Add(a, b);
var pow = Calculator<T>.Pow(a, b);
Run Code Online (Sandbox Code Playgroud)