Только вы можете сказать, будут ли динамические вызовы оператора соответствовать вашим требованиям к производительности, но, безусловно, можно решить некоторые из ваших проблем безопасности типов с помощью обобщений - нет никаких причин, по которым все должно проверяться при запуске время только из-за одного небольшого динамического вызова:
// 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));
}
}