在泛型方法签名中声明值元组

declare value tuples in generic method signature

我想创建能够通过匹配类型将列表转换为元组(或结构)的方法。 我还需要为多个元组组件计数创建重载,这样我就可以调用:

var t1 = GetAsTuple<(HealthComponent)>(components);
var t2 = GetAsTuple<(PositionComponent, ModelComponent)>(components);
var t3 = GetAsTuple<(Texture, ModelComponent, PositionComponent)>(components);

因为我有很多这些的组合,所以我称它们为组件元组,我想避免大量样板代码,并想要一种通用机制来填充这些元组。

这是我试过的,显然编译不了:

public T GetAsTuple<T>(Component[] components)
    where T : ValueTuple<x, y>, new()
    where x : Component
    where y : Component
{
    var t = new T();
    t.Item1 = components.OfType<x>().Single();
    t.Item2 = components.OfType<y>().Single();
    return t;
}

是否有可能是我正在尝试做的事情,或者是否有其他方法可以获得所需的行为。 我知道我可以通过反射达到我想要做的事情,但这对于上下文(游戏开发)来说太慢了。

您可以使用更简单的方法来获取特定组件:

public static class ComponentExtensions
{
    public static T GetComponent<T>(this Component[] components)
    {
        return components.OfType<T>().Single(); // note this will throw if 0 or more than 1 are in the array.
    }
}

那么我希望您 类 可以处理特定的事情并需要某些组件:

public class SomethingDoer
{
    private readonly PositionComponent _position;
    private readonly ModelComponent _model;

    public SomethingDoer(Component[] components)
    {
        _position = components.GetComponent<PositionComponent>();
        _model = components.GetComponent<ModelComponent>();
    }

    public void DoSomething()
    {
        ...
    }

按照您指定的方式获取这些元组的解决方案实际上需要许多非常相似的方法。如果您查看 .Net 源代码,您会发现 Tuple.Create 方法实际上是这样工作的:

var t1 = GetAsTuple<HealthComponent>(components);
var t2 = GetAsTuple<PositionComponent, ModelComponent>(components);
var t3 = GetAsTuple<Texture, ModelComponent, PositionComponent>(components);

public Tuple<T1> GetAsTuple<T1>(Component[] components)
    => Tuple.Create(components.GetComponent<T1>());

public Tuple<T1, T2> GetAsTuple<T1, T2>(Component[] components)
    => Tuple.Create(components.GetComponent<T1>(),
                    components.GetComponent<T2>());

public Tuple<T1, T2, T3> GetAsTuple<T1, T2, T3>(Component[] components)
    => Tuple.Create(components.GetComponent<T1>(),
                    components.GetComponent<T3>(),
                    components.GetComponent<T2>());

... etc ...

// I would still use this extension
public static class ComponentExtensions
{
    public static T GetComponent<T>(this Component[] components)
    {
        return components.OfType<T>().Single(); // note this will throw if 0 or more than 1 are in the array.
    }
}

现在我会坚持使用基于反射的解决方案。

当我有时间时,我将实现一个 T4 模板,它为我生成方法重载。