vendredi 9 février 2018

Navigate through nested classes and set values using reflection

I'm trying to build a UI which allows me to take a class of an unknown definition type (there will be a library of defined classes but they could have any structure and the code needs to deal with all of them generically), build input fields for population and then save the input fields back into the object.

An example object is something like this:

// This is the class which will be pulled apart and put back together with the new values...
public class Level1()
{
public Level1() {}
public Level2 Level2NestedClass1 {get;set;}
public Level2 Level2NestedClass2 {get;set;}
public string SomeText {get;set;}
}

// This class is nested within Level1
public class Level2()
{
public Level2() {}
public Level3 Level3NestedClass {get;set;}
public bool SomeBool {get;set;}
}

// This class is nested within Level2
public class Level3()
{
public Level3() {}
public string SomeMoreText {get;set;}
}

I've managed to get as far as creating input fields and saving the values for any properties on Level1 and any properties on Level2, but I can't get any further down the chain than that. Ideally it would need to be arbitrarily extensible.

The issue I'm having is I can't find a way to get to the nested properties and set the values on my "parent" class (Level1) because they are actually stored on Level2. e.g.

// This gets me the value of Level2 on Level1 so that I can set it
level1.GetType().GetProperty(level2.Name, BindingFlags.Public | BindingFlags.Instance)?.GetValue(level1)

I can't work out how to pull Level2 off of Level1, so that I could use this to then recursively loop up to the top level and set the objects as I go, e.g. set all values on Level3 then set Level3 on Level2 and then Level2 on Level1 so that the full nested hierarchy of values is maintained on the object at the end.

If I use Activator.CreateInstance to get the instance of Level2 I will lose all prior values I've set and only the last item set is saved. e.g.

var level2 = Activator.CreateInstance(level3.DeclaringType);

// This gets me the value of Level3 on Level2 so that I can set it
level2.GetType().GetProperty(level3.Name, BindingFlags.Public | BindingFlags.Instance)?.GetValue(level2)

Any tips or packages that might help would be appreciated. I've had a look all over google and stackoverflow but every example I've found uses known definitions and breadcrumb style lookups e.g. Level1.Level2.Level3 whereas I do not know the definition until runtime - I guess I could add attributes to the classes to define the breadcrumbs and use those?





Aucun commentaire:

Enregistrer un commentaire