C # 4.0 динамический:Потенциально эффективное решение для числовых обобщений?

StackOverflow https://stackoverflow.com/questions/5013883

Вопрос

После того, как я сам столкнулся с этой проблемой, пытаясь реализовать универсальный Vector2<int/float/double> в C # я провел множество исследований этой проблемы, также описанных в этом вопросе:

Менее универсальные дженерики?Возможное решение для арифметики в C # generics

Эти ссылки содержат еще некоторую справочную информацию и интересные подходы к решению:

https://jonskeet.uk/csharp/miscutil/usage/genericoperators.html

http://www.codeproject.com/KB/cs/genericnumerics.aspx

Теперь, когда вышел C # 4.0 с его новым универсальным dynamic тип, мой вопрос к блестящему сообществу 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>s к a Vector<string>.Обратите внимание, что ни одна из этих ошибок не была бы обнаружена во время компиляции вашим оригинальным решением.

Обратите внимание , что:

  1. Здесь ничто не мешает вам использовать дженерики но переход по маршруту компиляции дерева выражений для добавления вместо dynamic.Вызовы делегатов не являются бесплатными, но теоретически они должны быть быстрее, чем dynamic подход в этом случае - по крайней мере, вы избегаете боксирования типов значений.Только вы можете сказать, будут ли они быстрыми достаточно, хотя.

  2. Во всех случаях рассмотрите возможность написания статического конструктора, который проверяет, что аргумент типа на самом деле имеет подходящий оператор сложения, чтобы ошибки типа возникали на ранних этапах игры.


Редактировать (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));
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top