Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FieldInfo update subfield of field

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.

like image 310
Mrgnito Avatar asked Dec 23 '16 09:12

Mrgnito


1 Answers

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.

like image 80
Maksim Simkin Avatar answered Oct 14 '22 01:10

Maksim Simkin