GetProperties() to return all properties for an interface inheritance hierarchy

前端 未结 6 722
北海茫月
北海茫月 2020-11-27 02:24

Assuming the following hypothetical inheritance hierarchy:

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

public interface IB : IA
{
  string Name { get; se         


        
相关标签:
6条回答
  • 2020-11-27 03:03

    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);
    
    0 讨论(0)
  • 2020-11-27 03:08

    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());
    }
    
    0 讨论(0)
  • 2020-11-27 03:12

    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 :-)

    0 讨论(0)
  • 2020-11-27 03:13

    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);
    }
    
    0 讨论(0)
  • 2020-11-27 03:24

    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);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 03:26

    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)
    
    0 讨论(0)
提交回复
热议问题