【问题标题】:GetProperties() to return all properties for an interface inheritance hierarchyGetProperties() 返回接口继承层次结构的所有属性
【发布时间】:2008-12-11 09:51:00
【问题描述】:

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

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

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

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

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

只会产生接口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) 将返回“ID”和“Name”的PropertyInfo 对象数组。

有没有一种简单的方法可以像第一个示例那样在接口的继承层次结构中找到所有属性?

【问题讨论】:

    标签: c# .net reflection


    【解决方案1】:

    我已将@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);
    }
    

    【讨论】:

    • 纯粹的光彩!谢谢,这解决了我遇到的与操作员的问题类似的问题。
    • 您对 BindingFlags.FlattenHierarchy 的引用是多余的,因为您也在使用 BindingFlags.Instance。
    • 我已经实现了这个,但使用Stack&lt;Type&gt; 而不是Queue&lt;&gt;。使用堆栈,祖先保持这样的顺序:interface IFoo : IBar, IBaz where IBar : IBubble 和 'IBaz : IFlubber, the order of reflection becomes: IBar, IBubble, IBaz, IFlubber, IFoo`。
    • 不需要递归或队列,因为 GetInterfaces() 已经返回了一个类型实现的所有接口。正如 Marc 所说,没有层次结构,那么为什么我们必须“递归”任何事情呢?
    • @FrankyHollywood 这就是为什么你不使用GetProperties。您在起始类型上使用GetInterfaces,它将返回所有接口的扁平列表,并在每个接口上简单地执行GetProperties。不需要递归。接口中没有继承或基类型。
    【解决方案2】:

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

    【讨论】:

    • 这绝对是正确的答案!不需要笨重的递归。
    • 可靠的回答谢谢。我们如何在基本接口中获取属性的值?
    • @ilkerunal:通常的方式:在检索到的PropertyInfo 上调用GetValue,将您的实例(要获取的属性值)作为参数传递。示例:var list = new[] { 'a', 'b', 'c' }; var count = typeof(IList).GetPublicProperties().First(i =&gt; i.Name == "Count").GetValue(list); ← 将返回 3,即使 Count 定义在 ICollection 中,而不是 IList
    • 此解决方案存在缺陷,它可能会多次返回同名的属性。对于不同的属性列表,需要进一步清理结果。公认的答案是更正确的解决方案,因为它保证返回具有唯一名称的属性,并通过抓取继承链中最接近的属性来实现。
    • 如果type 是一个类,则不需要@AntWaters GetInterfaces,因为具体类必须实现所有的属性在继承链下游的所有接口中定义。在这种情况下使用 GetInterfaces 会导致 ALL 属性被复制。
    【解决方案3】:

    接口层次结构很痛苦——它们并没有真正“继承”,因为你可以有多个“父母”(为了更好的术语)。

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

    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 给予了应有的尊重,但这个答案也没有意识到 GetInterfaces() 已经返回了一个类型的所有实现的接口。正是因为没有“层次结构”,所以不需要递归或队列。
    • 我想知道在considered 中使用HashSet&lt;Type&gt; 是否比在这里使用List&lt;Type&gt; 更好?列表上的包含有一个循环,并且该循环放在一个 foreach 循环中,我相信如果有足够的项目并且代码应该非常快,这会损害性能。
    【解决方案4】:

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

    FlattenHierarchy 顺便说一句不起作用。 (仅在静态变量上。在智能感知中这么说)

    解决方法。谨防重复。

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

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        这在自定义 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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-12-22
          • 1970-01-01
          • 2011-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多