111

Assuming the following hypothetical inheritance hierarchy:

public interface IA
{
  int ID { get; set; }
}

public interface IB : IA
{
  string Name { get; set; }
}

Using reflection and making the following call:

typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance) 

will only yield the properties of interface IB, which is "Name".

If we were to do a similar test on the following code,

public abstract class A
{
  public int ID { get; set; }
}

public class B : A
{
  public string Name { get; set; }
}

the call typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance) will return an array of PropertyInfo objects for "ID" and "Name".

Is there an easy way to find all the properties in the inheritance hierarchy for interfaces as in the first example?

1
  • What I'm wondering is why the GetProperties() method behaves like this. I assume it was implemented like this intentionally. But why?
    – bgh
    Commented Nov 29, 2023 at 11:47

6 Answers 6

116

I've tweaked @Marc Gravel's example code into a useful extension method encapsulates both classes and interfaces. It also add's the interface properties first which I believe is the expected behaviour.

public static PropertyInfo[] GetPublicProperties(this Type type)
{
    if (type.IsInterface)
    {
        var propertyInfos = new List<PropertyInfo>();

        var considered = new List<Type>();
        var queue = new Queue<Type>();
        considered.Add(type);
        queue.Enqueue(type);
        while (queue.Count > 0)
        {
            var subType = queue.Dequeue();
            foreach (var subInterface in subType.GetInterfaces())
            {
                if (considered.Contains(subInterface)) continue;

                considered.Add(subInterface);
                queue.Enqueue(subInterface);
            }

            var typeProperties = subType.GetProperties(
                BindingFlags.FlattenHierarchy 
                | BindingFlags.Public 
                | BindingFlags.Instance);

            var newPropertyInfos = typeProperties
                .Where(x => !propertyInfos.Contains(x));

            propertyInfos.InsertRange(0, newPropertyInfos);
        }

        return propertyInfos.ToArray();
    }

    return type.GetProperties(BindingFlags.FlattenHierarchy
        | BindingFlags.Public | BindingFlags.Instance);
}
8
  • 2
    Pure Brilliance! Thank you this solved a problem I was having similar to the op's question.
    – kamui
    Commented Apr 12, 2012 at 13:00
  • 1
    Your references to BindingFlags.FlattenHierarchy are redundant seeing as you're also using BindingFlags.Instance.
    – Chris Ward
    Commented Sep 27, 2012 at 9:27
  • 1
    I've implemented this but with a Stack<Type> instead of a Queue<>. With a stack, the ancestry maintains an order such that interface IFoo : IBar, IBaz where IBar : IBubble and 'IBaz : IFlubber, the order of reflection becomes: IBar, IBubble, IBaz, IFlubber, IFoo`.
    – IAbstract
    Commented Oct 5, 2014 at 16:07
  • 4
    There is no need for recursion or queues since GetInterfaces() already returns all of the interfaces implemented by a type. As noted by Marc, there is no hierarchy, so why should we have to "recurse" on anything?
    – glopes
    Commented Aug 8, 2015 at 16:54
  • 3
    @FrankyHollywood that's why you don't use GetProperties. You use GetInterfaces on your starting type which will return the flattened list of all interfaces and simply do GetProperties on each interface. No need for recursion. There is no inheritance or base types in interfaces.
    – glopes
    Commented Aug 31, 2016 at 12:46
108

Type.GetInterfaces returns the flattened hierarchy, so there is no need for a recursive descent.

The entire method can be written much more concisely using LINQ:

public static IEnumerable<PropertyInfo> GetPublicProperties(this Type type)
{
    if (!type.IsInterface)
        return type.GetProperties();

    return (new Type[] { type })
           .Concat(type.GetInterfaces())
           .SelectMany(i => i.GetProperties());
}
10
  • 12
    This should definitely be the right answer! No need for the clunky recursion.
    – glopes
    Commented Aug 8, 2015 at 16:50
  • Solid answer thank you. How can we get value of a property in base interface?
    – ilker unal
    Commented Nov 4, 2015 at 14:30
  • 1
    @ilkerunal: The usual way: Call GetValue on the retrieved PropertyInfo, passing your instance (whose property value to get) as parameter. Example: var list = new[] { 'a', 'b', 'c' }; var count = typeof(IList).GetPublicProperties().First(i => i.Name == "Count").GetValue(list); ← will return 3, even though Count is defined within ICollection, not IList.
    – Douglas
    Commented Nov 6, 2015 at 19:13
  • 2
    This solution has flaws in that it may return properties of the same name multiple times. Further cleanup of the results are needed for a distinct property list. The accepted answer is the more correct solution as it guarantees returning properties with unique names and does so by grabbing the one closest in the inheritance chain. Commented Aug 24, 2017 at 17:43
  • 2
    @AntWaters GetInterfaces is not required if the type is a class, because the concrete class MUST implement all of the properties that are defined in all the Interfaces down the inheritance chain. Using GetInterfaces in that scenario would result in ALL properties being duplicated. Commented Mar 18, 2020 at 7:44
15

Interface hierarchies are a pain - they don't really "inherit" as such, since you can have multiple "parents" (for want of a better term).

"Flattening" (again, not quite the right term) the hierarchy might involve checking for all the interfaces that the interface implements and working from there...

interface ILow { void Low();}
interface IFoo : ILow { void Foo();}
interface IBar { void Bar();}
interface ITest : IFoo, IBar { void Test();}

static class Program
{
    static void Main()
    {
        List<Type> considered = new List<Type>();
        Queue<Type> queue = new Queue<Type>();
        considered.Add(typeof(ITest));
        queue.Enqueue(typeof(ITest));
        while (queue.Count > 0)
        {
            Type type = queue.Dequeue();
            Console.WriteLine("Considering " + type.Name);
            foreach (Type tmp in type.GetInterfaces())
            {
                if (!considered.Contains(tmp))
                {
                    considered.Add(tmp);
                    queue.Enqueue(tmp);
                }
            }
            foreach (var member in type.GetMembers())
            {
                Console.WriteLine(member.Name);
            }
        }
    }
}
2
  • 8
    I disagree. With all due respect for Marc, this answer also fails to realize that GetInterfaces() already returns all of the implemented interfaces for a type. Precisely because there is no "hierarchy", there is no need for recursion or queues.
    – glopes
    Commented Aug 8, 2015 at 16:52
  • I wonder if using a HashSet<Type> for considered is better than using List<Type> here? Contains on a List has a loop, and that loop is put in a foreach loop, I believe that would hurt the performance if there are enough items and the code should be critically fast.
    – Hopeless
    Commented Oct 5, 2020 at 3:40
4

Exactly the same problem has a workaround described here.

FlattenHierarchy doesnt work btw. (only on static vars. says so in intellisense)

Workaround. Beware of duplicates.

PropertyInfo[] pis = typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance);
Type[] tt = typeof(IB).GetInterfaces();
PropertyInfo[] pis2 = tt[0].GetProperties(BindingFlags.Public | BindingFlags.Instance);
2

Responding to @douglas and @user3524983, the following should answer the OP's question:

    static public IEnumerable<PropertyInfo> GetPropertiesAndInterfaceProperties(this Type type, BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.Instance)
    {
        if (!type.IsInterface) {
            return type.GetProperties( bindingAttr);
        }

        return type.GetInterfaces().Union(new Type[] { type }).SelectMany(i => i.GetProperties(bindingAttr)).Distinct();
    }

or, for an individual property:

    static public PropertyInfo GetPropertyOrInterfaceProperty(this Type type, string propertyName, BindingFlags bindingAttr = BindingFlags.Public|BindingFlags.Instance)
    {
        if (!type.IsInterface) {
            return type.GetProperty(propertyName, bindingAttr);
        }

        return type.GetInterfaces().Union(new Type[] { type }).Select(i => i.GetProperty( propertyName, bindingAttr)).Distinct().Where(propertyInfo => propertyInfo != null).Single();
    }

OK next time I'll debug it before posting instead of after :-)

1

this worked nicely and tersely for me in a custom MVC model binder. Should be able to extrapolate to any reflection scenario though. Still kind of stinks that it's too pass

    var props =  bindingContext.ModelType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).ToList();

    bindingContext.ModelType.GetInterfaces()
                      .ToList()
                      .ForEach(i => props.AddRange(i.GetProperties()));

    foreach (var property in props)

Not the answer you're looking for? Browse other questions tagged or ask your own question.