1

I have an issue. Say I have a Generic class which can have generic properties of other classes and can even have a list of other classes. If i have a function like

public void Read<T>() where T: class, new() 
{

    // Create an instance of our generic class
    var model = new T();
    var properties = typeof(T).GetProperties();

    // Loop through the objects properties
    for(var property in properties) {

        // Set our value
        SetPropertyValue(property, model, "test");
    }
}

private void SetPropertyValue(PropertyInfo property, object model, string value) {

    // Set our property value
    property.SetValue(model, value, null);
}

that would work if I had a class like this:

public class Person
{
    public string Name { get; set; }
}

and I invoked the Read method like this:

Read<Person>();

But if my Person model was like this:

public class Person
{
    public string Name { get; set; }
    public Company Company { get; set; }
}

public class Company 
{
    public string Name { get; set; }
}

And I tried to invoke the Read method again, it would fail because of the property have it's own list of properties. What would be better is if it traversed them too. Is there a way to do that?

r3plica
  • 13,017
  • 23
  • 128
  • 290
  • Just implement non-generic version of your Read method, in addition to generic one. From generic - call non-generic version to not repeat code too much. Then when you recurse - just call non-generic version with property type. Non-generic versions are very often good to have anyway. – Evk May 19 '16 at 12:35

2 Answers2

1

This answer can help. You should end with something like this:

if (t.IsPrimitive || t == typeof(Decimal) || t == typeof(String) || ... )
    SetPropertyValue(property, model, "test");
else
    // You will have to recode this line,
    // it's just to show you the idea how you can work around
    SetPropertyValue(property, model, Read.MakeGeneric(property.Type)());

You will also need to return your model variable from your Read method.

The condition is depending on what type you want to overwrite, if it's like on your example, you can change the condition to match only strings and add a check on the else to check property that are objects.

Community
  • 1
  • 1
romain-aga
  • 1,441
  • 9
  • 14
1

You can set the property value directly if it is a string, otherwise you can return value of a method similar to Read that takes Type as a parameter to create a model and fill its properties recursively.

public void Read<T>() where T : class, new()
{
    // Create an instance of our generic class
    var model = new T();
    var properties = typeof(T).GetProperties();

    // Loop through the objects properties
    foreach(var property in properties)
    {
        // Set our value
        SetPropertyValue(property, model, "test");
    }
}

private void SetPropertyValue(PropertyInfo property, object model, string value)
{
    if (property.PropertyType == typeof(string))
    {
        // Set our property value
        property.SetValue(model, value, null);
    }
    else
    {
        var submodel = Read(property.PropertyType);
        property.SetValue(model, submodel, null);
    }
}

private object Read(Type type)
{
    if (!IsTypeSupported(type))
    {
        throw new ArgumentException();
    }

    var model = type.GetConstructor(new Type[0]).Invoke(new object[0]);
    var properties = type.GetProperties();

    foreach (var property in properties)
    {
        SetPropertyValue(property, model, "test");
    }

    return model;
}

private bool IsTypeSupported(Type type)
{
    return type.IsClass && type.GetConstructor(new Type[0]) != null;
}
filhit
  • 2,084
  • 1
  • 21
  • 34