GetProperties () para retornar todas as propriedades de uma hierarquia de inheritance de interface

Assumindo a seguinte hierarquia de inheritance hipotética:

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

Usando reflection e fazendo a seguinte chamada:

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

só irá produzir as propriedades da interface IB , que é ” Name “.

Se fôssemos fazer um teste semelhante no código a seguir,

 public abstract class A { public int ID { get; set; } } public class B : A { public string Name { get; set; } } 

o tipo de chamada typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance) retornará uma matriz de objects PropertyInfo para ” ID ” e ” Name “.

Existe uma maneira fácil de encontrar todas as propriedades na hierarquia de inheritance para interfaces, como no primeiro exemplo?

Eu modifiquei o código de exemplo do @Marc Gravel em um método de extensão útil que encapsula classs e interfaces. Ele também adiciona as propriedades da interface primeiro, que acredito ser o comportamento esperado.

 public static PropertyInfo[] GetPublicProperties(this Type type) { if (type.IsInterface) { var propertyInfos = new List(); var considered = new List(); var queue = new Queue(); 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 retorna a hierarquia nivelada, portanto não há necessidade de uma descida recursiva.

Todo o método pode ser escrito de forma muito mais concisa usando o LINQ:

 public static IEnumerable GetPublicProperties(this Type type) { if (!type.IsInterface) return type.GetProperties(); return (new Type[] { type }) .Concat(type.GetInterfaces()) .SelectMany(i => i.GetProperties()); } 

Hierarquias de interface são um problema – elas não “herdam” de fato, já que você pode ter múltiplos “pais” (por falta de um termo melhor).

“Achatamento” (novamente, não exatamente o termo correto) a hierarquia pode envolver a verificação de todas as interfaces que a interface implementa e trabalha a partir daí …

 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 considered = new List(); Queue queue = new Queue(); 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); } } } } 

Exatamente o mesmo problema tem uma solução descrita aqui .

FlattenHierarchy não funciona btw. (somente em vars estáticos. diz isso em intellisense)

Solução alternativa. Cuidado com duplicatas.

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

isso funcionou bem e resumidamente para mim em um fichário de modelo MVC personalizado. Deve ser capaz de extrapolar para qualquer cenário de reflection. Ainda meio que fede que é também passar

  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) 

Respondendo a @douglas e @ user3524983, o seguinte deve responder à pergunta do OP:

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

ou, para uma propriedade individual:

  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 da próxima vez eu vou depurar antes de postar, em vez de depois 🙂