Search code examples
c#system.reflection

C# retrieve fields of type in a collection


I have this class:

internal class MyClass
{
    string name { get; set; }
    List<MyClass2> myClass2 { get; set; }
}

where MyClass2:

internal MyClass2
{
    string field1 { get; set; }
    string field2 { get; set; }
    int field3 { get; set; }
    long field4 { get; set; }
}

I want to retrieve the fields of the myClass2 via reflection, so I coded the snippet below:

Type objectType = typeof(MyClass);
IEnumerable<MemberInfo> members = objectType
    .GetMembers(BindingFlags.NonPublic | BindingFlags.Instance)
    .Where(p => p.MemberType == MemberTypes.Property);

foreach (var field in members)
{
    if (field.Name != "name")
    {
        var fieldT = field.GetType();
        Console.WriteLine("Field: " + field.Name + "; Field type: " + typeof(Property));
        Type propertiesType = field.GetType();
        IEnumerable<MemberInfo> propertiesMembers = typeof(Property)
            .GetMembers(BindingFlags.NonPublic | BindingFlags.Instance)
            .Where(p => p.MemberType == MemberTypes.Property);
                        
            foreach(var property in propertiesMembers)
            {
                Console.WriteLine("Looking for field: " + field.Name);
                var value = fields.Where(f => f.Key == field.Name).First();
                Console.WriteLine("Actual value receiver for: " + field.Name + " => " + value);
            }
      }
}

where fields it's just a Dictionary<string, dynamic> containing non interesting data for the question.

The thing is that I am unable to retrieve the fields in the variable myClass2, first, because it's a collection, and second, because in the fields variable of the loop, it's determining it's type to System.Reflection.RuntimePropertyInfo.

How can I modify my code to get a collection of the fields inside myClass2, yielding the real data that I am looking for? That is just the member info for the MemberTypes that are MemberTypes.Property?

Thanks.


Solution

  • The line var fieldT = field.GetType(); isn't doing what you think. field is a MemberInfo instance, so field.GetType() will be the same as typeof(MemberInfo).

    What you want is the type the property has been declared as, and for that, you'll need to cast to PropertyInfo first:

    Type propertiesType = ((PropertyInfo)field).PropertyType;
    

    From there, you can detect when the property is a List<T> and reflect over that T to get the list of properties on it:

    foreach (var property in members)
    {
        if (property.Name != "name")
        {
            //May be null for some built-in types.
            if (property.DeclaringType == null)
                continue;
    
            var propInfo = (PropertyInfo)property;
    
            //If this field refers to a List<T>, get the fields of that lists generic type
            if (propInfo.PropertyType.IsGenericType
                && typeof(IList<>).MakeGenericType(propInfo.PropertyType.GenericTypeArguments).IsAssignableFrom(propInfo.PropertyType))
            {
                var fieldType = propInfo.PropertyType.GenericTypeArguments[0];
                var genericListTypeFields = fieldType.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
    
                foreach (var innerField in genericListTypeFields)
                    Console.WriteLine($"Found property {innerField.Name}");
            }
        }
    }
    

    Which outputs:

    Found property field1
    Found property field2
    Found property field3
    Found property field4