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

假设以下假设的inheritance层次结构:

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

使用reflection并进行以下调用:

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

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

如果我们要对下面的代码进行类似的testing,

 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)的调用typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance)将返回“ ID ”和“ Name ”的PropertyInfo对象数组。

有没有简单的方法来find接口的inheritance层次中的所有属性,如第一个例子?

我已经调整了@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); } 

Type.GetInterfaces返回扁平层次结构,所以不需要recursion下降。

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

接口层次结构是一个痛苦 – 他们并不真正“inheritance”,因为你可以有多个“父母”(为了一个更好的术语)。

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

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

完全相同的问题有这里描述的解决方法。

FlattenHierarchy不工作顺便说一句。 (只在静态variables上,intellisense中是这么说的)

解决方法。 谨防重复。

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

这在我自定义的MVC模型活页夹中很好,很简洁。 应该能够外推到任何reflection场景。 仍然有点臭,它太过分了

  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) 

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

好的,下次我会在发布之前debugging它,而不是之后:-)