如何创建一个在C#中返回泛型类型的泛型函数

时间:2014-05-17 21:28:52

标签: c# generics

尝试在C#中创建一个通用的lerp函数。我的功能目前看起来如下所示。

public T Lerp<T>(T lhs, T rhs, float t)
{
    return (1.0f - t) * lhs + t * rhs;
}

引用为重复的问题限制了它们键入内置类型。我不想在C#中返回我想要返回类型为T(通用对象?)的对象的约束。

1 个答案:

答案 0 :(得分:3)

要做到这一点需要几个步骤。我们会删除引用关键字,因为在这种情况下它们无法提供帮助。让我们假设您想要LERP向量。让我们假设你有一组名为Vector2,Vector3和Vector4的矢量对象(分别用于2维向量,3d和4d)。要使Lerp功能适用于所有这三个,您必须先调整这些对象。

为执行向量和浮点数之间的乘法的所有三个创建基类。基类调用一个抽象方法,该方法在特定向量对象中执行实际数学运算。基类看起来像这样:

public abstract BaseVector
{
    public abstract BaseVector Multiply(float multiplier);

    public static BaseVector operator *(BaseVector v, float multiplier)
    {
        return v.Multiply(multiplier);
    }

    // Add the operator * overload where the multiplier is first
}

现在,对于函数本身而言,提供where子句以利用基类的行为是一个问题。如果您的Vector2,Vector3和Vector4类都扩展了BaseVector并实现了Multiply()方法,那么这将有效:

public T Lerp<T>(T lhs, T rhs, float t)
    where T : BaseVector
{
    return (1.0f - t) * lhs + t * rhs;
}

它无法获得更多的泛型,因为运算符重载是必须在它们适用的类中声明的静态扩展方法。您将能够使用接口,然后可以使用实现该接口的任何对象,但您必须直接调用接口中声明的Multiply方法。在这种情况下,您可以更改where子句以使T实现接口。它的语法相同,只是类型从BaseVector更改为接口名称。

除非您指定where子句,否则您无法在基础Object类型中不存在的对象上调用方法名称。


来自programmers.stackexchange.com的回答:

如果您的目的是弄清楚如何使用任何传入类型实现LERP函数,那么您的问题可能更适合Stackoverflow.com。

由于线性插值通常使用浮点数来实现,因此在这种情况下使用泛型可能是最干净的而不是。结果是一个浮点数:

// Precise method which guarantees v = v1 when t = 1.
public static float Lerp(float v0, float v1, float t)
{
    return (1-t)*v0 + t*v1;
}

如果您认为通过将所有类型更改为double可能会有用,则可以提高精度。这使得简单的功能简单地操作。您遇到的问题是运算符重载是静态的,因此如果您想创建一个对多个对象(例如向量)进行操作的函数,您必须创建一个基类,所有这些objects extends定义operator*重载并提供where子句以将T限制为扩展该基类的内容。这可能会变得复杂。

这个答案的目的是让你思考你为何感受到你:

  1. 需要该功能是通用的
  2. 需要ref关键字
  3. 两者都使一个非常简单的功能复杂化。 ref关键字是不必要的,因为您只读取参数。在处理数值时,通用是不必要的,因为在提高精度时它们都具有隐式转换运算符。当您降低精度时,它们也都具有显式转换运算符。简而言之,除非您使用复杂类型,否则请保持方法非常简单,只有在您真正需要时才能打破更复杂的技巧。