GetProperties () возвращает все свойства для иерархии наследования интерфейса

Предполагая следующую гипотетическую иерархию наследования:

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)возвратит массив PropertyInfoобъектов для " ID" и " Name".

Есть ли простой способ найти все свойства в иерархии наследования для интерфейсов, как в первом примере?

11.12.2008 09:51:00
6 ОТВЕТОВ
РЕШЕНИЕ

Я добавил пример кода @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);
}
109
14.03.2010 22:36:44
Чистый блеск! Спасибо, это решило проблему, которая у меня была похожа на вопрос оп.
kamui 12.04.2012 13:00:55
Ваши ссылки на BindingFlags.FlattenHierarchy являются избыточными, поскольку вы также используете BindingFlags.Instance.
Chris Ward 27.09.2012 09:27:16
Я реализовал это, но с Stack<Type>вместо Queue<>. Со стеком родословная поддерживает такой порядок, что interface IFoo : IBar, IBazгде IBar : IBubbleи 'IBaz: IFlubber , the order of reflection becomes: IBar , IBubble , IBaz , IFlubber , IFoo`.
IAbstract 5.10.2014 16:07:10
Нет необходимости в рекурсии или очередях, поскольку GetInterfaces () уже возвращает все интерфейсы, реализованные типом. Как отметил Марк, иерархии нет, так почему мы должны «рекурсировать» что-либо?
glopes 8.08.2015 16:54:30
@FrankyHollywood, поэтому вы не используете GetProperties. Вы используете GetInterfacesна своем начальном типе, который возвратит плоский список всех интерфейсов и просто сделает GetPropertiesна каждом интерфейсе. Нет необходимости в рекурсии. В интерфейсах нет наследования или базовых типов.
glopes 31.08.2016 12:46:32

Иерархии интерфейсов - это боль - они на самом деле не «наследуют» как таковые, поскольку у вас может быть несколько «родителей» (если не хватает лучшего термина).

«Сглаживание» (опять же, не совсем правильный термин) иерархия может включать проверку всех интерфейсов, которые реализует интерфейс, и оттуда работать ...

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);
            }
        }
    }
}
15
11.12.2008 10:08:01
Я не согласен. При всем уважении к Марку, этот ответ также не может понять, что GetInterfaces () уже возвращает все реализованные интерфейсы для типа. Именно потому, что нет «иерархии», нет необходимости в рекурсии или очередях.
glopes 8.08.2015 16:52:51

Точно такая же проблема имеет обходной путь, описанный здесь .

Кстати, FlattenHierarchy не работает. (только для статических переменных. Так сказано в IntelliSense)

Обходной путь. Остерегайтесь дубликатов.

PropertyInfo[] pis = typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance);
Type[] tt = typeof(IB).GetInterfaces();
PropertyInfo[] pis2 = tt[0].GetProperties(BindingFlags.Public | BindingFlags.Instance);
3
17.02.2017 14:37:48

это работало хорошо и лаконично для меня в пользовательском переплете модели 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)
1
14.12.2012 14:38:46

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());
}
73
5.11.2014 20:13:13
Это определенно должен быть правильный ответ! Нет необходимости в неуклюжей рекурсии.
glopes 8.08.2015 16:50:51
Твердый ответ спасибо. Как мы можем получить значение свойства в базовом интерфейсе?
ilker unal 4.11.2015 14:30:47
@ilkerunal: Обычный способ: вызвать GetValueизвлеченный PropertyInfo, передав ваш экземпляр (значение свойства которого нужно получить) в качестве параметра. Пример: var list = new[] { 'a', 'b', 'c' }; var count = typeof(IList).GetPublicProperties().First(i => i.Name == "Count").GetValue(list);← вернет 3, хотя Countопределено внутри ICollection, а не IList.
Douglas 6.11.2015 19:13:43
Это решение имеет недостатки в том, что оно может возвращать свойства одного и того же имени несколько раз. Дальнейшая очистка результатов необходима для отдельного списка свойств. Принятый ответ является более правильным решением, так как он гарантирует возврат свойств с уникальными именами и делает это путем захвата ближайшего в цепочке наследования.
user3524983 24.08.2017 17:43:39
@AntWaters GetInterfaces не требуется, если typeкласс является классом, потому что конкретный класс ДОЛЖЕН реализовать все свойства, которые определены во всех интерфейсах в цепочке наследования. Использование GetInterfacesв этом сценарии приведет к дублированию ВСЕХ свойств.
Chris Schaller 18.03.2020 07:44:38

Отвечая на @douglas и @ user3524983, следующее должно ответить на вопрос ОП:

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

Хорошо, в следующий раз я буду отлаживать его перед публикацией, а не после :-)

2
14.11.2017 04:26:40