108

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?

Ray Hayes
  • 14,896
  • 8
  • 53
  • 78
sduplooy
  • 14,340
  • 9
  • 41
  • 60

6 Answers6

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);
}
mythz
  • 141,670
  • 29
  • 246
  • 390
  • 2
    Pure Brilliance! Thank you this solved a problem I was having similar to the op's question. – kamui Apr 12 '12 at 13:00
  • 1
    Your references to BindingFlags.FlattenHierarchy are redundant seeing as you're also using BindingFlags.Instance. – Chris Ward Sep 27 '12 at 09:27
  • 1
    I've implemented this but with a `Stack` 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 Oct 05 '14 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 Aug 08 '15 at 16:54
  • @glopes, because properties in basetypes of interfaces are not returned by 'GetProperties'. Not even with the 'FlattenHierarchy' flag. – FrankyHollywood Aug 31 '16 at 11:18
  • 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 Aug 31 '16 at 12:46
  • @glopes Would you mind posting your solution as an answer? – Jeff Feb 11 '19 at 18:59
  • @ChrisWard No, `BindingFlags.FlattenHierarchy` specifies static members, and `BindingFlags.Instance` specifies instance members. The two are mutually exclusive. – Jeff Feb 11 '19 at 20:31
103

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());
}
Douglas
  • 53,759
  • 13
  • 140
  • 188
  • 11
    This should definitely be the right answer! No need for the clunky recursion. – glopes Aug 08 '15 at 16:50
  • Solid answer thank you. How can we get value of a property in base interface? – ilker unal Nov 04 '15 at 14:30
  • 1
    @ilkerunal: The usual way: Call [`GetValue`](https://msdn.microsoft.com/en-us/library/hh194385%28v=vs.110%29.aspx) 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 Nov 06 '15 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. – user3524983 Aug 24 '17 at 17:43
  • @user3524983: Could you please give an example? I just tried it and only got one property for a given name. – Douglas Aug 26 '17 at 18:51
  • `interface Base { string String { get; } }` `interface Derived : Base { string String { get; } }` `var propertyInfos = GetPublicProperties(typeof(Derived));` `foreach (var propertyInfo in propertyInfos) Console.WriteLine("Name: " + propertyInfo.Name);` – Jeff Feb 11 '19 at 19:23
  • @Douglas I think that is what @user3524983 is referring to. `Name: String` is printed twice. However, the same happens with the accepted solution... – Jeff Feb 11 '19 at 19:29
  • @Jeff: Thanks for clarifying. However, that would be the expected behavior, since those are two distinct properties that share the same `Name` (but different [`DeclaringType`](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.memberinfo.declaringtype)). Even the compiler warns that `Derived.String` hides inherited member `Base.String`. An implementing type is free to provide different explicit implementations for the two, so it would be misleading to return just one. `public class Implementation : Derived { string Base.String => "B"; string Derived.String => "D"; }` – Douglas Feb 11 '19 at 23:34
  • Does anyone know what this will return if `type` is a `class` that inherits interfaces (perhaps further up the hierarchy)? Is there a need to call `GetInterfaces` even for a class? – Ant Waters May 02 '19 at 08:51
  • 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. – Chris Schaller Mar 18 '20 at 07: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);
            }
        }
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 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 Aug 08 '15 at 16:52
  • I wonder if using a `HashSet` for `considered` is better than using `List` 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 Oct 05 '20 at 03:40
3

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);
takrl
  • 6,356
  • 3
  • 60
  • 69
Wolf5
  • 16,600
  • 12
  • 59
  • 58
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 :-)

sjb-sjb
  • 1,112
  • 6
  • 14
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)