0

I want to create methods which are able to convert lists to tuples (or a struct) by matching types. I would also need to create overloads for several tuple component counts so I could for example call:

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

Since I have lots of combinations of these I call them component-tuples I want to avoind lots of boilerplate code and want a generic mechanism to fill these tuples.

This is what I tried, obviously it does not compile:

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

Is it possible what I am trying to do or is there another way to get the desired behavior. I know that I could reach what Iam trying to do with reflection, but this is too slow for the context (game development).

codymanix
  • 28,510
  • 21
  • 92
  • 151
  • Without knowing why you want these as Tuple values I don't have the full picture. But I would almost always, and I include the word almost for an unknown reason as I mean always, avoid the use of Tuples. Do they really need to be Tuples or could you have a specific variable for each component you want, making for a much simpler method to extract them from your array. – Steve Harris Mar 26 '21 at 17:19
  • I need it for the implemenation of an entity component system. I could also use classes or structs instead of tuples but this would make it even more difficult to create generic code which initializes them with values. – codymanix Mar 26 '21 at 17:23
  • I'm not sure it would, you could pass the components array into the constructor of your class and initialise properties by calling your method. – Steve Harris Mar 26 '21 at 17:25
  • What about `public (T1, T2) GetAsTuple(...) { return (components.OfType().Single(), components.OfType().Single()); }`? Unclear what you are trying to do. Remember you must at least know the n-ary of the generic type, in other words how many types it has – Charlieface Mar 26 '21 at 17:29
  • I edited the question to make things a bit more clear – codymanix Mar 26 '21 at 17:30

3 Answers3

1

A solution to get these Tuples as you've specified would actually require many, very similar methods. If you look into the .Net source code you'll see that this is actually how the Tuple.Create method works:

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.
    }
}
Steve Harris
  • 5,014
  • 1
  • 10
  • 25
0

For now I will stick with a reflection based solution.

When I have the time I will implement a T4-template which generates the method overloads for me.

codymanix
  • 28,510
  • 21
  • 92
  • 151
-1

You could have a much simpler method to get the specific component:

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

Then I expect you'd have classes that deal with specific things and need certain components:

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()
    {
        ...
    }
Steve Harris
  • 5,014
  • 1
  • 10
  • 25
  • This would force me to write the same code over and over again for every combination of component types. I need a generic way to accomplish exactly this. – codymanix Mar 26 '21 at 17:37
  • Again I'm not certain exactly why you'd need to do that but I think that you'd probably only need a small number of classes like this. I've added another answer anyway. – Steve Harris Mar 26 '21 at 17:41