2008-12-11 21 views
75

Suponiendo que la siguiente jerarquía de herencia hipotético:GetProperties() para devolver todas las propiedades para una jerarquía de herencia interfaz

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

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

Uso de reflexión y haciendo que la llamada siguiente:

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

sólo habrá producir las propiedades de interfaz IB, que es "Name".

Si tuviéramos que hacer una prueba similar en el siguiente código,

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

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

la llamada typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance) devuelve un vector de objetos PropertyInfo para "ID" y "Name".

¿Hay una manera fácil de encontrar todas las propiedades en la jerarquía de herencia para las interfaces como en el primer ejemplo?

Respuesta

99

he pellizcado código de ejemplo de @Marc grava en un método útil extensión encapsula ambas clases e interfaces. También agrega las propiedades de interfaz primero, que creo que es el comportamiento esperado.

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

Pure Brilliance! Gracias, esto resolvió un problema que estaba teniendo similar a la pregunta de la operación. – kamui

+1

No hay suficientes votaciones en el mundo para esto. – Chao

+1

Sus referencias a BindingFlags.FlattenHierarchy son redundantes ya que también está utilizando BindingFlags.Instance. –

15

Las jerarquías de interfaz son un problema - en realidad no "heredan" como tal, ya que puede tener varios "padres" (a falta de un mejor término).

"aplanamiento" (de nuevo, no es el término correcto) la jerarquía podría implicar la comprobación de todas las interfaces que implementa el interfaz y trabajar desde allí ...

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

No estoy de acuerdo. Con el debido respeto por Marc, esta respuesta tampoco se da cuenta de que GetInterfaces() ya devuelve todas las interfaces implementadas para un tipo. Precisamente porque no hay una "jerarquía", no hay necesidad de recursión o colas. – glopes

3

Exactamente el mismo problema tiene una solución descrito here.

FlattenHierarchy doesnt work by btw. (solo en vars estáticos, lo dice en intellisense)

Solución. Cuidado con los duplicados.

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

Esto funcionó bien y concisamente para mí en una carpeta de encargo modelo MVC. Sin embargo, debería ser capaz de extrapolar a cualquier escenario de reflexión. Todavía tipo de apesta que es demasiado pasar

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 devuelve la jerarquía aplanada, lo que no hay necesidad de un descenso recursivo.

Todo el método se puede escribir mucho más concisa utilizando 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()); 
} 
+6

¡Definitivamente esta debería ser la respuesta correcta! No hay necesidad de la recurrencia torpe. – glopes

+0

Respuesta sólida gracias. ¿Cómo podemos obtener el valor de una propiedad en la interfaz base? –

+1

@ilkerunal: La forma habitual: Llamar a ['GetValue'] (https://msdn.microsoft.com/en-us/library/hh194385%28v=vs.110%29.aspx) en el' PropertyInfo' recuperado, pasando su instancia (cuyo valor de propiedad para obtener) como parámetro. Ejemplo: 'var list = new [] {'a', 'b', 'c'}; var count = typeof (IList) .GetPublicProperties(). Primero (i => i.Name == "Count"). GetValue (list); '← devolverá 3, aunque' Count' se defina dentro de 'ICollection', no 'IList'. – Douglas

0

respuesta a @douglas y @ user3524983, el siguiente debe responder a la pregunta de la 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(); 
    } 

o, por un la propiedad 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 próxima vez voy a depurarlo antes de la publicación en lugar de después :-)

Cuestiones relacionadas