FieldInfo update subfield of field

784 Views Asked by At

Good day,

I need to make function that will iterate on Dictionary that stores variable name and variable`s new value. After that, I need to update class variable with that value.

void UpdateValues(Type type, Dictionary<string, string> values)
{
    foreach (var value in values)
    {
        var fieldInfo = selected.GetComponent(type).GetType().GetField(value.Key);
        if (fieldInfo == null) continue;

        fieldInfo.SetValue(selected.GetComponent(type), value.Value);
    }
}

It works but I want little improvement and I absolutely don't know if it is possible. As you can see, that function can accept any class, not just one specific.

If I have class like this

class test
{
    public string age;
}

And I would use function this way, it would work.

UpdateValues(typeof(test), new Dictionary<string, string>{{"age", "17"}});

Problem is if I have class like this and I would like to update "subfield" (field in field)

class test
{
    public string age;
}

class test2
{
    public test data;
}

I was thinking that syntax could be something like this, but I have no idea how could I do it.

UpdateValues(typeof(test2), new Dictionary<string, string>{{"data.age", "17"}});

To sum it up, I need to make function that will take class that is stored in another class. Function will iterate trough the dictionary and update fields of class and even her subfields.

2

There are 2 best solutions below

1
On BEST ANSWER

I would propose to add a recursive call to your method, to set the properties. I have changed your method a little bit, because i don't have selected object, it takes an object as a parameter

void UpdateValues<T>(T obj,  Dictionary<string, string> values)
{
    foreach (var value in values)
    {       
        SetProperty(obj, value.Key, value.Value);
    }
}


public void SetProperty<T>( T obj, string valueKey, string value, Type type= null)
{
    var typeToUse = type ?? typeof(T);
    var pointIndex = valueKey.IndexOf(".");
    if (pointIndex!=-1)
    {
        var subKey = valueKey.Substring(0, pointIndex);
        var fieldInfo = typeToUse.GetField(subKey);
        var propObj =  fieldInfo.GetValue(obj)
                        ?? Activator.CreateInstance(fieldInfo.FieldType);           
        SetProperty(propObj, valueKey.Substring(pointIndex+1), value, fieldInfo.FieldType);
        fieldInfo.SetValue(obj, propObj);
    }
    else
    {       
        var fieldInfo = typeToUse.GetField(valueKey);       
        if (fieldInfo != null)
            fieldInfo.SetValue(obj, value);
    }
}

It works even if you define

class test3
{
    public test2 data;
}

and call

UpdateValues(t, new Dictionary<string, string>{{"age", "17"}}); 
UpdateValues(t2, new Dictionary<string, string> { { "data.age", "17" } });
UpdateValues(t3, new Dictionary<string, string> { { "data.data.age", "17" } });

The third parameter of SetProperty method is not really nice, i would avoid it, but i don't know how to solve it with generics, after creating with Activator you get object as a Type, and object doesn't have field age

You are using Dictionary<string, string> as a parameter that allows you to set only string fields, so you must assume that you don't have any other. Actually this will work even if you will use Dictionary<string, object>, that i would suggest to do.

0
On

First of all you will need to change your Dictionary variable to use Dictionary<string, object> if you want to pass a class as a parameter in here. Secondly Here is an example of how to make it work.

class test
{
    public string age;
}

class test2
{
    public test data;
}

Lets suppose i have created an instance of test class and added it in a dictionary, to get the fields with reflection and then update the instance of test2 accordingly.

    public void UpdateValues(object test2, Dictionary<string, object> dict)
    {
        var fieldValues = test2.GetType()
                   .GetFields()
                   .ToList();
        foreach (var value in dict)
        {
            foreach (var field in fieldValues)
            {


                if (value.Key == field.Name)
                {
                    bool obj = field.FieldType == typeof(test);
                    if (obj)
                    {
                        if (dict.ContainsKey("data")) 
                        {
                        var prop = test2.GetType().GetField("data", System.Reflection.BindingFlags.Public
                        | System.Reflection.BindingFlags.Instance);
                        prop.SetValue(test2, dict["data"]);
                        break;
                        }
                    }
                    else
                    {
                        var prop = test2.GetType().GetField(value.Key, System.Reflection.BindingFlags.Public
                        | System.Reflection.BindingFlags.Instance);
                        prop.SetValue(test2, value.Value);
                        break;
                    }
                }
            }

        }
    }

In the end call you function i have created a Dictionary<string,object> instance to send it as a parameter to the function

 object test2 = new test2();
 test t = new test();
 t.age = "10";
 Dictionary<string, object> dict = new Dictionary<string, object>();
 dict.Add("data", t);
 UpdateValues(test2, dict);