How can I recursively search properties in C# only if those properties inherit from some base class?
-
15-04-2021 - |
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
?
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