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

sdu*_*ooy 88 .net c# reflection

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

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

public interface IB : IA
{
  string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance) 
Run Code Online (Sandbox Code Playgroud)

只会产生接口的属性IB,即" Name".

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

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

public class B : A
{
  public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

该调用typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance)将返回PropertyInfo" ID"和" Name" 的对象数组.

有没有一种简单的方法来查找接口的继承层次结构中的所有属性,如第一个示例中所示?

myt*_*thz 108

我已经将@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);
}
Run Code Online (Sandbox Code Playgroud)

  • 由于GetInterfaces()已经返回由类型实现的所有接口,因此不需要递归或队列.正如马克所说,没有等级制度,那么为什么我们要对任何东西进行"递归"呢? (4认同)
  • @FrankyHollywood这就是你不使用`GetProperties`的原因.您在起始类型上使用`GetInterfaces`,它将返回所有接口的展平列表,并在每个接口上执行`GetProperties`.不需要递归.接口中没有继承或基类型. (3认同)
  • 纯粹的光彩!谢谢,这解决了我遇到的与操作问题类似的问题。 (2认同)

Dou*_*las 68

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());
}
Run Code Online (Sandbox Code Playgroud)

  • 这肯定是正确的答案!不需要笨重的递归. (7认同)
  • 此解决方案存在缺陷,因为它可能会多次返回相同名称的属性。对于不同的属性列表,需要进一步清理结果。接受的答案是更正确的解决方案,因为它保证返回具有唯一名称的属性,并且通过获取继承链中最接近的属性来实现。 (2认同)
  • @AntWaters 如果“type”是一个类,则不需要“GetInterfaces”,因为具体类_必须_实现继承链上所有接口中定义的属性的__all__。在这种情况下使用“GetInterfaces”将导致 _ALL_ 属性被重复。 (2认同)

Mar*_*ell 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我不同意.尽管对Marc充满敬意,但这个答案也没有意识到GetInterfaces()已经返回了一个类型的所有实现接口.正因为没有"层次结构",所以不需要递归或队列. (7认同)