C#4.0动态:数字泛型的潜在高效解决方案?

问题描述 投票:8回答:1

在尝试在C#中实现通用Vector2<int/float/double>后遇到这个问题后,我对这个问题进行了一系列调查,这个问题也有描述:

Less generic generics? A possible solution for arithmetic in C# generics

这些链接包含更多背景信息和迷人的解决方案方法:

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

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

现在C#4.0已经推出了新的多功能dynamic类型,我对这个辉煌的SO社区的问题是:它是一个可以用来构建高性能,通用Vector / Matrix /等的工具。数字类型?

显然,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>混合来执行我们的数学运算,就像使用ints一样?

也许某种形式的铸造可以用来确保this.X是一个T,虽然我不知道它会如何表现。

generics dynamic c#-4.0 type-constraints
1个回答
7
投票

只有你可以判断动态操作员调用是否符合你的性能要求,但是通过泛型来解决你的一些类型安全问题当然是可能的 - 没有理由因为一个小的动态调用而在运行时必须检查所有内容:

// 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>。请注意,在原始解决方案的编译时,不会捕获这些错误。

注意:

  1. 没有什么可以阻止你在这里使用泛型,而是沿着compiling-an-expression-tree路线添加而不是dynamic。委托调用不是免费的,但理论上它们应该比dynamic方法更快 - 在这种情况下 - 至少,你避免拳击值类型。但是,只有你可以判断它们是否足够快。
  2. 在所有情况下,考虑编写一个静态构造函数来验证type-argument实际上是否具有合适的加法运算符,以便在游戏的早期发生类型错误。

编辑(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));
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.