2008-12-11 2 views
75

Предполагая следующую гипотетическую иерархию наследования: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».

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

ответ

99

Я отлажены пример кода @Marc Гравий в в полезный метод расширения инкапсулирует как классы и интерфейсы. Это также добавляет свойства интерфейса, которые, как я считаю, являются ожидаемым поведением.

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

Чистый блеск! Спасибо, что я решил проблему, связанную с вопросом о. – kamui 2012-04-12 13:00:55

+1

Для этого недостаточно во всём мире. – Chao 2012-05-11 15:39:30

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

Я не согласен. При всем уважении к Marc этот ответ также не понимает, что GetInterfaces() уже возвращает все реализованные интерфейсы для типа. Именно потому, что нет «иерархии», нет необходимости в рекурсии или очередях. – glopes 2015-08-08 16:52:51

3

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

FlattenHierarchy does not work btw. (только на статических vars. говорит так в intellisense)

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

PropertyInfo[] pis = typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance); 
Type[] tt = typeof(IB).GetInterfaces(); 
PropertyInfo[] pis2 = tt[0].GetProperties(BindingFlags.Public | BindingFlags.Instance); 
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) 
42

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

В ответ на @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(); 
    } 

OK В следующий раз я отлажу его перед публикацией а не после :-)