Question

How can I recursively get all the properties of an object only if the type of the property inherits from some base class?

This was my attempt:

static IEnumerable<PropertyInfo> FindProperties(object objectTree, Type targetType)
{
    if (objectTree.GetType().IsAssignableFrom(targetType))
    {
        var properties = objectTree.GetType().GetProperties();
        foreach (var property in properties)
        {
            yield return property;
        }

        foreach (var property in FindProperties(properties, targetType))
        {
            yield return property;
        }
    }
}

So I could call,

var allPropertiesOfPageTypes = FindProperties(someClass, typeof(Page));

However, the number of properties returned is always zero. What am I doing wrong?

Edit:

I'm not sure if this matters but the subclasses are generic classes:

public abstract class MasterPage<T> : BasePage<T> where T : MasterPage<T>

That inherits:

public abstract class BasePage<T> : Page where T : BasePage<T>

Things that inherit from Master/BasePage seem to be returning false for IsAssignableFrom?

Was it helpful?

Solution

You need to only recurse when you have the right type, and you need an instance, not the property itself:

static IEnumerable<PropertyInfo> FindProperties(object objectTree, Type targetType)
{
    if (targetType.IsAssignableFrom(objectTree.GetType()))
    {
        var properties = objectTree.GetType().GetProperties();
        foreach (var property in properties)
        {
            yield return property;

            if (targetType.IsAssignableFrom(property.PropertyType))
            {
                object instance = property.GetValue(objectTree, null);
                foreach (var subproperty in FindProperties(instance, targetType))
                {
                    yield return subproperty;
                }
            }
        }
    }
}

OTHER TIPS

In order to verify if an object inherits from another class you have to do the opposite of what you are doing:

 if (targetType.IsAssignableFrom(objectTree.GetType()))

this works in a similar way to:

Parent o = new Derived();

Maybe this could work?

public static LinkedPageElement<TElement> GetLinkedElement<TPage, TElement>(Page page, bool verbose = true) where TElement : class
{
    var propInfos = page.GetType().GetProperties();

    // First try to find the property in the current page type
    foreach (var propInfo in propInfos)
    {
        var attributes = propInfo.GetCustomAttributes(typeof(LinkedPageAttribute), true);
        if (attributes.Length == 0) continue;

        var linkedPageAttribute = (from a in attributes where a.GetType() == typeof(LinkedPageAttribute) select a).FirstOrDefault();
        if (linkedPageAttribute == null || !(linkedPageAttribute is LinkedPageAttribute)) continue;

        if ((linkedPageAttribute as LinkedPageAttribute).PageType == typeof(TPage))
        {
            return new LinkedPageElement<TElement>
            {
                Element = propInfo.GetValue(page, null) as TElement,
                AutoClick = (linkedPageAttribute as LinkedPageAttribute).AutoClick
            };
        }
    }

    // Then try to find it in a property
    var containedInProperty = propInfos.Where(x => x.PropertyType.IsSubclassOf(typeof(Page)))
        .Select(x => GetLinkedElement<TPage, TElement>((Page)x.GetValue(page, null), false))
        .FirstOrDefault(x => x != null);

    if (containedInProperty != null) return containedInProperty;

    // you are trying to navigate to a page which cannot be reached from the current page, check to see you have a link with LinkedPage attribute
    if (verbose)
        throw new ArgumentException("You don't have a link to this page {0} from this page {1}".FormatWith(typeof(TPage), page.GetType()));

    return null;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top