Я хочу иметь 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
}
}
*/
Есть какой-то умный способ реализовать это, или это абсолютно невозможно?
Извините, что не очень ясно, но позвольте мне объяснить, в чем проблема.
Подумайте об использовании обычных обобщений C #. Метод GetLength () не будет компилироваться, потому что все типы, которые я хочу использовать (int, float, double, long), должны будут использовать интерфейс, который Math.Pow () должен принять в качестве параметра.
Буквальная замена токена «T» именами типов решит эту проблему, увеличит гибкость, достигнет производительности рукописного кода и ускорит разработку.
Я сделал свой собственный генератор шаблонов, который генерирует код C # путем написания кода C # 🙂
http://www.youtube.com/watch?v=Uz868MuVvTY
К сожалению, дженерики в C # сильно отличаются от шаблонов в C ++. Для этого используется общий интерфейс (например, IArithmetic
) должен был бы существовать (который был высоко востребован, но не реализован)* для разных типов, и это не в рамках сейчас.
Это можно сделать с помощью генерации кода и Шаблоны T4, однако, но это требует генерации кода для каждого типа на основе общего «шаблона».
* Примечание. Запрос на подключение заблокирован, по крайней мере, временно.
Два решения этой проблемы:
Создайте абстрактный класс или интерфейсный калькулятор [t] и реализуйте его для типов, которые вам интересны. Передайте экземпляр калькулятора вашим векторным классам, чтобы они могли использовать его для выполнения математических операций.
Используя деревья выражений, вы на самом деле можете создать калькулятор статического класса [t], в котором есть методы типа add, pow и т. Д. В статическом конструкторе, вы можете скомпилировать динамические выражения и методы static вызывают эти скомпилированные лямбда-выражения. При таком подходе вам не нужно реализовывать калькулятор для каждого типа или передавать его (так как он статический).
Например:
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);