web-dev-qa-db-fra.com

GetProperties () pour renvoyer toutes les propriétés d'une hiérarchie d'héritage d'interface

En supposant la hiérarchie d'héritage hypothétique suivante:

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

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

En utilisant la réflexion et en effectuant l'appel suivant: 

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

donnera uniquement les propriétés de l'interface IB, qui est "Name". 

Si nous devions faire un test similaire sur le code suivant,

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

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

l'appel typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance) retournera un tableau d'objets PropertyInfo pour "ID" et "Name".

Existe-t-il un moyen simple de rechercher toutes les propriétés dans la hiérarchie d'héritage pour les interfaces, comme dans le premier exemple?

82
sduplooy

J'ai modifié l'exemple de code de @Marc Gravel en une méthode d'extension utile qui encapsule les classes et les interfaces. Il ajoute également les propriétés d'interface en premier qui, je crois, est le comportement attendu.

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);
}
105
mythz

Type.GetInterfaces renvoie la hiérarchie aplatie, une descente récursive n'est donc pas nécessaire. 

Toute la méthode peut être écrite beaucoup plus concement en utilisant 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());
}
59
Douglas

Les hiérarchies d'interface sont pénibles - elles ne "héritent" pas vraiment en tant que telles, car vous pouvez avoir plusieurs "parents" (à défaut d'un meilleur terme).

"Aplatissement" (encore une fois, ce n'est pas tout à fait le terme qui convient), la hiérarchie pourrait impliquer de vérifier toutes les interfaces implémentées par l'interface et de travailler à partir de là ...

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
Marc Gravell

Exactement le même problème a une solution de contournement décrite ici .

FlattenHierarchy ne fonctionne pas btw. (seulement sur les vars statiques. le dit en intellisense)

Solution de contournement. Méfiez-vous des doublons.

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

cela a fonctionné joliment pour moi dans un classeur de modèle MVC personnalisé. Devrait pouvoir extrapoler à n'importe quel scénario de réflexion cependant. Encore genre de puant que c'est trop passer

    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
Derek Strickland

En réponse à @douglas et @ user3524983, les éléments suivants devraient répondre à la question du PO 

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

ou, pour une propriété individuelle: 

    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 prochaine fois, je vais le déboguer avant de poster au lieu d’après :-) 

0
sjb-sjb