Home > Net >  C# retrieve fields of type in a collection
C# retrieve fields of type in a collection

Time:08-09

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.

CodePudding user response:

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

CodePudding user response:

In order to analyze the properties of List<MyClass2>, you can use the following, flexible approach that checks whether a property is assignable to IEnumerable<T>. If this is the case, it analyzes T (which is MyClass2 in your case.

public static void Main()
{
    var fields = new Dictionary<string, dynamic>();
    AnalyzeProperties(typeof(MyClass), fields, "name");
}

private static void AnalyzeProperties(Type objectType, IDictionary<string, dynamic> fields, params string[] excludedFields)
{
    var members = objectType
        .GetProperties(BindingFlags.Public | BindingFlags.Instance);

    foreach (var field in members)
    {
        if (excludedFields.Contains(field.Name))
            continue;
        var fieldT = field.PropertyType;
        Console.WriteLine("Field: "   field.Name   "; Field type: "   fieldT);
        // Check whether type is assignable to IEnumerable<T>
        Type genTypeParam;
        if (fieldT.IsGenericType 
            && (genTypeParam = fieldT.GenericTypeArguments.SingleOrDefault()) != null
            && typeof(IEnumerable<>).MakeGenericType(genTypeParam).IsAssignableFrom(fieldT))
        {
            Console.WriteLine(fieldT   " is an IEnumerable<"   genTypeParam   ">, analyzing "   genTypeParam);
            AnalyzeProperties(genTypeParam, fields);
        }
        else 
        {
            // Analyze property type
            Console.WriteLine("Looking for field: "   field.Name);
            var value = fields.Where(f => f.Key == field.Name).FirstOrDefault();
            Console.WriteLine("Actual value receiver for: "   field.Name   " => "   value);
        }
    }
}

You can check the code using this fiddle.

Please note that I've made some adjustments to your sample in order to make the property reflection working:

  • Changed fields to properties
  • Used GetProperties instead of GetMembers
  • Changed binding to Public

Please take the code as a sample and adjust the code to your needs.

  • Related