views:

2414

answers:

1

Hi,

Got a complex reflection question. Given the code below how would you implement the pseudo so that given an instance of Parent it will enumerate over the types Properties find child objects with a Property of the same type as Parent and set the reference to the provided p. Hope that makes sense. Also I need this to work with Generic lists as well. See below for sample object graph. After running this every Person in the child Pet instances will be the Parent instance.

 public class ChildSetter<Parent>
    {
        public void Set(Parent p)
        {

            //pseudo 
            //var parentName = p.GetType().Name;
            //foreach (var property in p.Properties)
            //{
            //   if (!property.IsList)
            //   {
            //      if (property.ContainsProperty(parentName))
            //          property.Properties[parentName] = p;
            //   }
            //   else
            //   {
            //      if (property.ListType.ContainsProperty(parentName))
            //      {
            //          foreach (var item in property)
            //          {
            //              item.Properties[parentName] = p;
            //          }
            //      }
            //   }
            //}
        }
    }

public class Person
{
    public Pet Pet { get; set; }
    public IList<Pet> Pets { get; set; }

}

public class Pet
{
    public Person Person { get; set; }
}

A non generic example of this code is below:

 public void Set(Person p)
    {
        p.Pet.Person = p;
        foreach (var pet in p.Pets)
        {
            pet.Person = p;
        }
    }
+1  A: 

I haven't tested it, but how about:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

static class Program
{
    static void Main()
    {
        ...
    }
    public static void SetParent<T>(T root)
    {
        foreach (PropertyInfo prop in typeof(T).GetProperties
            (BindingFlags.Public | BindingFlags.Instance))
        {
            if (!prop.CanRead) continue;

            Type listType = null;
            foreach (Type interfaceType in prop.PropertyType.GetInterfaces())
            {
                if (interfaceType.IsGenericType &&
                    interfaceType.GetGenericTypeDefinition() == typeof(IList<>))
                { // IList<T> detected
                    listType = interfaceType.GetGenericArguments()[0];
                }
            }

            List<PropertyInfo> propsToSet = new List<PropertyInfo>();
            foreach (PropertyInfo childProp in (listType ?? prop.PropertyType).GetProperties(
                BindingFlags.Public | BindingFlags.Instance))
            {
                if (childProp.PropertyType == typeof(T)) propsToSet.Add(childProp);
            }

            if(propsToSet.Count == 0) continue; // nothing to do
            if (listType == null)
            {
                object child = prop.GetValue(root, null);
                if (child == null) continue;
                foreach (PropertyInfo childProp in propsToSet)
                {
                    childProp.SetValue(child, root, null);
                }
            }
            else
            {
                IList list = (IList)prop.GetValue(root, null);
                foreach (object child in list)
                {
                    if (child == null) continue;
                    foreach (PropertyInfo childProp in propsToSet)
                    {
                        childProp.SetValue(child, root, null);
                    }
                }
            }
        }
    }
}
Marc Gravell
Thanks Marc I will check it out
bleevo
It probably needs a null-check after getting the list (before the foreach), and perhaps also check childProp.CanWrite when filling propsToSet.
Marc Gravell
I had to change this line from interfaceType.GetGenericTypeDefinition() == typeof(IList<>))to interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))Apart from that it works 100% thanks heaps. I will see if this is sane to use now :)
bleevo