Как я могу рекурсивно искать свойства в C#, только если эти свойства наследуются от некоторого базового класса?

Как я могу рекурсивно получить все свойства объекта, только если тип свойства наследуется от некоторого базового класса?

Это была моя попытка:

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;
        }
    }
}

Чтобы я мог позвонить,

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

Однако количество возвращаемых свойств всегда равно нулю. Что я делаю не так?

Редактировать:

Я не уверен, что это имеет значение, но подклассы являются общими классами:

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

Это наследует:

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

Вещи, которые наследуются от Master/BasePage, похоже, возвращают false для IsAssignableFrom?


person KingNestor    schedule 11.01.2012    source источник
comment
Вы пробовали .OfType<T> от linq?   -  person Anatolii Gabuza    schedule 11.01.2012
comment
Одна секунда, я собираюсь опубликовать свою попытку ..   -  person KingNestor    schedule 11.01.2012
comment
anatoliiG - Я думаю, поскольку OfType дает объекты определенного типа, это не будет здесь применимо - например, во всех задействованных коллекциях есть элементы типа PropertyInfo?   -  person Kieren Johnstone    schedule 11.01.2012


Ответы (3)


Вам нужно рекурсивно только тогда, когда у вас есть правильный тип, и вам нужен экземпляр, а не само свойство:

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;
                }
            }
        }
    }
}
person Keith    schedule 11.01.2012
comment
Теперь в методе, который вызывает это, мне нужно получить значение, но у меня больше нет объекта, из которого оно пришло. Любая идея, как я мог бы сохранить объект, из которого он был получен, поэтому, когда я перехожу к propInfo.GetValue(page, null) как TElement, он не взорвется? - person KingNestor; 11.01.2012
comment
@KingNestor - я бы в любом случае вернул значение (сделайте это IEnumerable<TElement>), и если вам нужно, чтобы тип свойства можно было назначать из TElement, вам следует проверить, что свойство также возвращает этот тип, что-то вроде typeof(TElement).IsAssignableFrom(subproperty.PropertyType) - person Keith; 11.01.2012

Чтобы проверить, наследуется ли объект от другого класса, вам нужно сделать противоположное тому, что вы делаете:

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

это работает аналогично:

Parent o = new Derived();
person loodakrawa    schedule 11.01.2012

Может, это сработает?

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;
}
person A. Tapper    schedule 11.01.2012