Frage GetProperties (), um alle Eigenschaften für eine Schnittstellenvererbungshierarchie zurückzugeben


Annahme der folgenden hypothetischen Vererbungshierarchie:

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

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

Nachdenken und den folgenden Anruf machen:

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

wird nur die Eigenschaften der Schnittstelle ergeben IB, welches ist "Name".

Wenn wir einen ähnlichen Test für den folgenden Code durchführen würden,

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

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

der Anruf typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance) gibt ein Array von PropertyInfo Objekte für "ID" und "Name".

Gibt es eine einfache Möglichkeit, alle Eigenschaften in der Vererbungshierarchie für Schnittstellen wie im ersten Beispiel zu finden?


76
2017-12-11 09:51


Ursprung


Antworten:


Ich habe den Beispielcode von @Marc Gravel in eine nützliche Erweiterungsmethode umgewandelt, die sowohl Klassen als auch Schnittstellen kapselt. Es fügt auch die Schnittstelleneigenschaften zuerst hinzu, von denen ich glaube, dass sie das erwartete Verhalten sind.

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 gibt die abgeflachte Hierarchie zurück, so dass kein rekursiver Abstieg erforderlich ist.

Die gesamte Methode kann mit LINQ viel prägnanter geschrieben werden:

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



Schnittstellenhierarchien sind ein Schmerz - sie "erben" nicht wirklich als solche, da Sie mehrere "Eltern" haben können (mangels eines besseren Begriffs).

"Abflachung" (wieder, nicht ganz der richtige Begriff), die Hierarchie könnte beinhalten, nach allen Schnittstellen zu suchen, die die Schnittstelle implementiert und von dort aus arbeitet ...

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



Genau das gleiche Problem hat eine Umgehung beschrieben Hier.

FlattenHierarchy funktioniert übrigens nicht. (nur auf statischen vars. sagt so in intellisense)

Problemumgehung Vorsicht vor Duplikaten.

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



das funktionierte schön und knapp für mich in einem kundenspezifischen MVC-Modellbinder. Sollte jedoch in der Lage sein, auf irgendein Reflexionsszenario zu extrapolieren. Immer noch stinkt es, dass es zu pass ist

    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



Wenn Sie auf @douglas und @ user3524983 antworten, sollte Folgendes die Frage des OP beantworten:

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

oder, für eine einzelne Eigenschaft:

    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, nächstes Mal werde ich es debuggen, bevor ich poste statt nach :-)


0
2017-11-14 04:13