GetProperties()返回接口继承层次结构的所有属性


97

假设以下假设继承层次结构:

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

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

使用反射并进行以下调用:

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

只会产生interface的属性IB,即“ Name”。

如果我们要对以下代码进行类似的测试,

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

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

该调用typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance)将返回PropertyInfoID”和“ Name” 的对象数组。

是否像第一个示例一样,在接口的继承层次结构中找到所有属性的简便方法?

Answers:


112

我已经将@Marc Gravel的示例代码调整为一个有用的扩展方法,该方法封装了类和接口。我也首先添加了接口属性,我认为这是预期的行为。

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

2
纯净的光彩!谢谢您,这解决了我遇到的与操作员问题类似的问题。
kamui 2012年

1
您对BindingFlags.FlattenHierarchy的引用是多余的,因为您也在使用BindingFlags.Instance。
克里斯·沃德

1
我已经实现了,但是用Stack<Type>代替了Queue<>。对于堆栈,祖先会保持以下顺序:interface IFoo : IBar, IBazwhere IBar : IBubble和'IBaz:IFlubber , the order of reflection becomes: IBar , IBubble , IBaz , IFlubber , IFoo`。
IAbstract 2014年

4
不需要递归或排队,因为GetInterfaces()已经返回了由类型实现的所有接口。正如Marc所述,没有层次结构,那么为什么我们必须对任何东西进行“递归”?
2015年

3
@FrankyHollywood这就是为什么您不使用GetProperties。你用GetInterfaces你的启动类型将返回所有接口的平面化列表,并简单地做GetProperties每个接口上。无需递归。接口中没有继承或基本类型。
glopes '16

78

Type.GetInterfaces 返回扁平化的层次结构,因此不需要递归下降。

使用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());
}

8
这绝对是正确的答案!无需笨拙的递归。
2015年

扎实的回答,谢谢。我们如何在基本接口中获取属性的值?
ilker unal

1
@ilkerunal:通常的方法:调用GetValue检索到的PropertyInfo,将您的实例(要获取其属性值)作为参数传递。示例:var list = new[] { 'a', 'b', 'c' }; var count = typeof(IList).GetPublicProperties().First(i => i.Name == "Count").GetValue(list);即使在Count中定义了ICollection,←也将返回3 IList
道格拉斯

2
该解决方案的缺陷在于它可能多次返回相同名称的属性。对于不同的属性列表,需要进一步清理结果。可接受的答案是更正确的解决方案,因为它可以确保返回具有唯一名称的属性,并且可以通过获取继承链中最接近的名称来做到这一点。
user3524983

1
@AntWaters GetInterfaces 如果不要求type是一类,因为具体类,必须实现所有在所有的接口定义沿着继承链的属性。GetInterfaces在那种情况下使用将导致所有属性重复。
克里斯·谢勒

15

接口层次结构很痛苦-它们并不是真正的“继承”,因为您可以有多个“父级”(为了更好的用语)。

层次结构的“平淡化”(再次,不是很正确的术语)可能涉及检查接口实现的所有接口并从那里开始工作...

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

7
我不同意。出于对Marc的所有应有的尊重,此答案还没有意识到GetInterfaces()已返回该类型的所有已实现接口。正是因为没有“层次结构”,所以不需要递归或排队。
2015年

3

完全相同的问题具有此处描述的解决方法

FlattenHierarchy不起作用。(仅适用于静态变量。在智能感知中这样说)

解决方法。当心重复项。

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

2

响应@douglas和@ user3524983,以下内容应回答OP的问题:

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

或者,对于单个财产:

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

好的,下一次我将在发布之前而不是之后进行调试:-)


1

在自定义MVC模型绑定程序中,这对我来说非常好用。虽然应该能够推断出任何反射情况。仍然有点太臭了

    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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.