简体   繁体   中英

Add dynamic type to List<object> using reflection

I am creating a Unity custom window, This window displays data from from an object that will eventually be converted to JSON .

I am able to read the data and modify it as long as it isn't an array which is where I am having issues.

The data looks like this:

public static class GameData {
    private static SaveData data;

    public static SaveData save { get { return data.save; } }

    [System.Serializable]
    public class SaveData {
        public int energyCurrent = 100;
        public float speed = 2.5f;
        public List<int> itm = new List<int>() { 1, 2, 3, 4, 5, 6 };
    }
}

I then have an object which stores each item like this (where they key is the field name and value is the field value; ex: key=speed , value=2.5f ):

class KeyValue {
    public string key;
    public object value;

    public KeyValue(string key, object value) {
        this.key = key;
        this.value = value;
    }
}

It is then stored within a list:

List<KeyValue> keyValues = new List<KeyValue>();

The part I am having issues with is the save which looks like this:

void SaveDataLocal() {
    keyValues.ForEach(item => {
        // GameData.save is a reference to SaveData
        var field = GameData.save.GetType().GetField(item.key);
        field.SetValue(GameData.save, GetValue(item));
    });
    GameData.Save();
}

object GetValue(KeyValue keyVal) {
    var value = keyVal.value;
    if (value.GetType().IsArray || isList(value)) {
        List<object> list = new List<object>();
        ((List<KeyValue>)value).ForEach(item => {
            list.add(GetValue(item));
        });
        return list;
    } else {
        return value;
    }
}

I have tried two ways to update the value, however, I am getting an error saying:

ArgumentException: Object type System.Collections.Generic.List`1[System.Object] cannot be converted to target type: System.Collections.Generic.List`1[System.Int32]

I am trying to use Reflection because list type could be int , float , Vector3 , etc. so I need it to by dynamic, and I cannot use a dynamic type because we are not using 4.x .

The error is taking place here:

field.SetValue(GameData.save, GetValue(item));

GetValue() returning a List<object> when the field is a List<int> . What can I do to pass the List<object> ?

Edit

I have tried casting it like so:

public static T Cast<T>(object o) {
    return (T)o;
}

keyValues.ForEach(item => {
    var field = GameData.save.GetType().GetField(item.key);
    var val = GetValue(item);

    var castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(field.FieldType);
    var r = castMethod.Invoke(null, new object[] { val });

    field.SetValue(GameData.save, r);
});

But I am getting this error:

InvalidCastException: Cannot cast from source type to destination type. GameSmart.SaveData.Cast[List`1]

I am not sure if this is the cleanest way, but I basically had to do a cast by looping over all the items in the list/array and individually casting them. I can't seem to cast an entire list at once.

public static List<T> CastList<T>(List<object> o) {
    var list = new List<T>();
    foreach (var i in o) { list.Add((T)i); }
    return list;
}

void SaveDataLocal() {
    keyValues.ForEach(item => {
        var field = GameData.save.GetType().GetField(item.key);
        var value = GetValue(item);

        if (value.GetType().IsArray || isList(value)) {
            Type type;
            if (isList(value)) type = field.FieldType.GetGenericArguments().Single();
            else type = field.FieldType.GetElementType();
            var castMethod = this.GetType().GetMethod("CastList").MakeGenericMethod(type);
            value = castMethod.Invoke(null, new object[] { value });
        }

        field.SetValue(GameData.save, value);
    });
    GameData.Save();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM