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