在通用方法签名中声明值元组

问题描述

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

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;
}

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

解决方法

获取您指定的这些元组的解决方案实际上需要许多非常相似的方法。如果您查看 .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,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 模板,它为我生成方法重载。

,

您可以有一个更简单的方法来获取特定组件:

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()
    {
        ...
    }