John
John

Reputation: 273

Get all properties and subproperties from a class

I am using reflection to get a class name, and need to get all sub properties of the class, and all the sub properties' properties.

I am running into a recursion issue where the items get added to the incorrect list.

My code is as follows:

private List<Member> GetMembers(object instance)
{
    var memberList = new List<Member>();
    var childMembers = new List<Member>();

    foreach (var propertyInfo in instance.GetType().GetProperties())
    {
        var member = new Member
        {
            Name = propertyInfo.PropertyType.IsList() ? propertyInfo.Name + "[]" : propertyInfo.Name,
            Type = SetPropertyType(propertyInfo.PropertyType),
        };

        if (propertyInfo.PropertyType.IsEnum)
        {
            member.Members = GetEnumValues(propertyInfo).ToArray();
        }

        if (propertyInfo.PropertyType.BaseType == typeof(ModelBase))
        {
            var childInstance = propertyInfo.GetValue(instance) ?? Activator.CreateInstance(propertyInfo.PropertyType);

            childMembers.AddRange(GetMembers(childInstance));

            member.Members = childMembers.ToArray();
        }

        if (propertyInfo.PropertyType.IsGenericType && (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>) ||
            propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
        {
            var itemType = propertyInfo.PropertyType.GetGenericArguments()[0];

            var childInstance = Activator.CreateInstance(itemType);

            childMembers.AddRange(GetMembers(childInstance));

            member.Members = childMembers.Distinct().ToArray();
        }

        memberList.Add(member);
    }

    return memberList;
}

Upvotes: 0

Views: 664

Answers (2)

Shelby115
Shelby115

Reputation: 2857

I can't know for certain since I don't have the knowledge of your code to debug and test it; however, I believe your problem may be stemming from the fact that you're re-using the childMembers list. Let me know if this is not the case.

private List<Member> GetMembers(object instance)
{
    var memberList = new List<Member>();

    foreach (var propertyInfo in instance.GetType().GetProperties())
    {
        var childMembers = new List<Member>(); // Moved to here, so it's not shared among all propertyInfo iterations.
        var member = new Member
        {
            Name = propertyInfo.PropertyType.IsList() ? propertyInfo.Name + "[]" : propertyInfo.Name,
            Type = SetPropertyType(propertyInfo.PropertyType),
        };

        if (propertyInfo.PropertyType.IsEnum)
        {
            member.Members = GetEnumValues(propertyInfo).ToArray();
        }

        if (propertyInfo.PropertyType.BaseType == typeof(ModelBase))
        {
            var childInstance = propertyInfo.GetValue(instance) ?? Activator.CreateInstance(propertyInfo.PropertyType);

            childMembers.AddRange(GetMembers(childInstance));

            member.Members = childMembers.ToArray();
        }

        if (propertyInfo.PropertyType.IsGenericType && (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>) ||
            propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
        {
            var itemType = propertyInfo.PropertyType.GetGenericArguments()[0];

            var childInstance = Activator.CreateInstance(itemType);

            childMembers.AddRange(GetMembers(childInstance));

            member.Members = childMembers.Distinct().ToArray();
        }

        memberList.Add(member);
    }

    return memberList;
}

Upvotes: 1

InBetween
InBetween

Reputation: 32740

Wouldn't the following do?

public static IEnumerable<PropertyInfo> GetProperties(this Type type, int depth = 1)
{
    IEnumerable<PropertyInfo> getProperties(Type currentType, int currentDepth)
    {
        if (currentDepth >= depth)
            yield break;

        foreach (var property in currentType.GetProperties())
        {
            yield return property;

            foreach (var subProperty in getProperties(property.PropertyType,
                                                      currentDepth + 1))
            {
                yield return subProperty;
            }
        }
    }

    if (depth < 1)
        throw new ArgumentOutOfRangeException(nameof(depth));

    return getProperties(type, 0);    
}

Given the following type:

class Foo
{
    public string S { get; }
    public int I { get; }
}

The output of

Console.WriteLine(string.Join(Environment.NewLine,
                  typeof(Foo).GetProperties(2)
                             .Select(p => $"{p.DeclaringType.Name}: {p.Name}")));

would be:

Foo: S
String: Chars
String: Length
Foo: I

Upvotes: 0

Related Questions