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 ofGetMembers
- Changed binding to
Public
Please take the code as a sample and adjust the code to your needs.