Home > OS >  Is there a way to iterate through list of generic values in Unity/c#?
Is there a way to iterate through list of generic values in Unity/c#?

Time:03-20

Is it possible to iterate through a several generic instances and do something with them?

public class Dynamic<T>
{
    public T value;
    public T defaultVal;

    public void SetDefaultValue()
    {
        value = defaultVal;
    }
}

Is it even possible to make it look something like this:

List<Dynamic<T>> dynamics = new List<Dynamics<T>>();

dynamics.Add(new Dynamic<float>());
dynamics.Add(new Dynamic<Vector2>());
///...

foreach (var item in dynamics)
{
    item.SetDefaultValue();
}

And yeah, its probably useless at all since I have to manually insert these values in that list, but I really want to know if its even possible.

CodePudding user response:

With the data model you have, in c# 2.0 there is no way to do what you want other than by declaring dynamics as a List<object> and using reflection:

List<object> dynamics = new List<object>();

dynamics.Add(new Dynamic<float>());
dynamics.Add(new Dynamic<Vector2>());

foreach (var item in dynamics)
{
    item.GetType().GetMethod("SetDefaultValue").Invoke(item, null);
}

Not really recommended, since the performance will be poor, and you lose compile-time checking for correctness.

Instead, consider introducing a non-generic interface that allows accessing required members of Dynamic<T> in a non-generic manner:

public interface IDynamic
{
    // Provide read-only access to the Value and DefaultValue as objects:
    object Value { get; }
    object DefaultValue { get; } 
    // Set the value to the default value:
    void SetDefaultValue();
}

public class Dynamic<T> : IDynamic
{
    public T value;
    public T defaultVal;

    public void SetDefaultValue()
    {
        value = defaultVal;
    }

    // Use explicit interface implementation because we don't want people to use the non-generic properties when working directly with a specific Dynamic<T>:
    object IDynamic.Value { get { return value; } } // Note that if T is a struct the value will get boxed
    object IDynamic.DefaultValue { get { return defaultVal; } } // Note that if T is a struct the value will get boxed
}

Now you will be able to do:

List<IDynamic> dynamics = new List<IDynamic>();

dynamics.Add(new Dynamic<float>());
dynamics.Add(new Dynamic<Vector2>());

foreach (var item in dynamics)
{
    item.SetDefaultValue();
}

CodePudding user response:

Extending dbc's answer, to have the properties of your class stored in a list to change them at once and by reference you need to wrap them into a class, for this case Dynamic<whatever>, because if you have primitive/value types, those will be copied into the list, and even the property values are changed looping through the list you would be changing the list copies and not the class properties themselves. Like so:

using System;
using System.Collections.Generic;
using UnityEngine;

public class GenericTrial2 : MonoBehaviour {
    Dynamic<float> vertical = new Dynamic<float> { value = 17, defaultVal = 1 };
    Dynamic<Vector2> run = new Dynamic<Vector2> { value = new Vector2(17, 17), defaultVal = Vector2.one };
    Dynamic<Quaternion> rotation= new Dynamic<Quaternion> { value = new Quaternion(4,4,4,4), defaultVal = Quaternion.identity };
    List<IDynamic> dynamics;

    private void Start() {
        dynamics = new List<IDynamic>();
        dynamics.Add(vertical);
        dynamics.Add(run);
        dynamics.Add(rotation);
        dynamics.ForEach(prop => { Debug.Log(prop.Value.ToString()); });
        Debug.Log($"initValues: vertical: {vertical.value.ToString()}, "  
            $"run: {run.value.ToString()}, rot: {rotation.value.ToString()} ");
    }

    void resetProps() {
        foreach (var item in dynamics) {
            item.SetDefaultValue();
        }
        dynamics.ForEach(prop => { Debug.Log(prop.Value.ToString()); });
    }

    private void Update() {
        if (Input.GetKeyDown(KeyCode.Space)) {
            resetProps();
            Debug.Log($"after reset: vertical: {vertical.value.ToString()}, "  
                $"run: {run.value.ToString()}, rot: {rotation.value.ToString()} ");
        }
    }
}
public interface IDynamic {
    // Provide read-only access to the Value and DefaultValue as objects:
    object Value { get; }
    object DefaultValue { get; }
    // Set the value to the default value:
    void SetDefaultValue();
}

public class Dynamic<T> : IDynamic {
    public T value;
    public T defaultVal;

    public void SetDefaultValue() {
        value = defaultVal;
    }

    // Use explicit interface implementation because we don't want people to use the non-generic properties when working directly with a specific Dynamic<T>:
    object IDynamic.Value { get { return value; } } // Note that if T is a struct the value will get boxed
    object IDynamic.DefaultValue { get { return defaultVal; } } // Note that if T is a struct the value will get boxed
}

Output:

initValues: vertical: 17, run: (17.0, 17.0), rot: (4.0, 4.0, 4.0, 4.0)   
after reset: vertical: 1, run: (1.0, 1.0), rot: (0.0, 0.0, 0.0, 1.0) 
  • Related