Pregunta GetProperties () para devolver todas las propiedades para una jerarquía de herencia de interfaz


Suponiendo la siguiente jerarquía hipotética de herencia:

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

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

Usando la reflexión y haciendo la siguiente llamada:

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

solo cederá las propiedades de la interfaz IB, cual 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) devolverá una serie de PropertyInfo objetos para "ID"y"Name".

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


76
2017-12-11 09:51


origen


Respuestas:


He modificado el código de ejemplo de @Marc Gravel en un método de extensión útil que encapsula las clases y las 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);
}

102
2018-03-14 22:36



Type.GetInterfaces devuelve la jerarquía aplanada, por lo que no hay necesidad de un descenso recursivo.

El método completo se puede escribir mucho más concisamente 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());
}

49
2017-11-05 20:13



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

"Aplanar" (una vez más, no del todo el término correcto) la jerarquía podría implicar verificar todas las interfaces que implementa la 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);
            }
        }
    }
}

15
2017-12-11 10:02



Exactamente el mismo problema tiene una solución alternativa descrita aquí.

FlattenHierarchy no funciona por cierto. (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);

3
2017-12-11 10:06



esto funcionó bien y lacónico para mí en una carpeta modelo MVC personalizada. Sin embargo, debería ser capaz de extrapolar a cualquier escenario de reflexión. Todavía huele mal que es demasiado pasado

    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
2017-12-14 14:38



Respondiendo a @douglas y @ user3524983, lo siguiente debe responder la pregunta del 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, para una 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 la próxima vez lo depuraré antes de publicar en lugar de después :-)


0
2017-11-14 04:13