Получить все объявления базовых свойств в иерархии классов - PullRequest
2 голосов
/ 21 сентября 2010

Рассмотрим этот интересный набор типов:

class A     { public virtual     int MyProperty { get; set; } }
class B : A { public override    int MyProperty { get; set; } }
class C : B { public new virtual int MyProperty { get; set; } }
class D : C { public override    int MyProperty { get; set; } }
class E : D { public new         int MyProperty { get; set; } }

Я вижу здесь три разных свойства с пятью реализациями, скрывающими или перекрывающими друг друга.

Я пытаюсь получить наборсвойство объявления для типа E:

A.MyProperty
C.MyProperty
E.MyProperty

Но мой код ниже дает мне набор свойств реализаций :

A.MyProperty
B.MyProperty
C.MyProperty
D.MyProperty
E.MyProperty

Что мне нужно сделать, чтобы получить объявления свойств?

Или есть ли шанс, что B.MyProperty когда-либо вернет значение, отличное от A.MyProperty, для любого экземпляра E?

Если мой подход движется в неправильном направлении: как получить все элементы свойств типа, включая скрытые, но не включая те, которые никогда не будут иметь разных значений?


void GetProperties(Type type)
{
    if (type.BaseType != null)
    {
        GetProperties(type.BaseType);
    }

    foreach (var item in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
    {
        Console.WriteLine("{0}.{1}", type.Name, item.Name);
    }
}

Желаемые выходы:

typeof(A)       typeof(B)       typeof(C)       typeof(D)       typeof(E)
------------    ------------    ------------    ------------    ------------
A.MyProperty    A.MyProperty    A.MyProperty    A.MyProperty    A.MyProperty
                                C.MyProperty    C.MyProperty    C.MyProperty
                                                                E.MyProperty

Ответы [ 3 ]

2 голосов
/ 21 сентября 2010

Это может помочь вам начать путь, который вы хотите:

    static void GetProperties(Type type)
    {
        if (type.BaseType != null)
        {
            GetProperties(type.BaseType);
        }

        foreach (var item in type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public))
        {
            MethodInfo method = item.GetGetMethod();
            MethodInfo baseMethod = method.GetBaseDefinition();

            System.Diagnostics.Debug.WriteLine(string.Format("{0} {1}.{2} {3}.{4}", type.Name, method.DeclaringType.Name, method.Name, baseMethod.DeclaringType, baseMethod.Name));

            if (baseMethod.DeclaringType == type)
            {
                Console.WriteLine("{0} {1}", type.Name, item.Name);
            }
        }
    }

Этот код выводит следующее:

MyProperty

C MyProperty

E MyProperty

Обратите внимание, что этот код зависит от использования MethodInfo метода get, связанного со свойством. Если у вас есть свойства только для набора, вам нужно будет выполнить некоторые дополнительные проверки для обработки этого случая.

1 голос
/ 21 сентября 2010

но не включая те, которые никогда не будут иметь других значений?

Похоже, вы ожидаете, что система Reflection включит правила для этого особого случая. В противном случае кто-то другой пожаловался бы, что свойства B и D отсутствуют.

Но я думаю, что ответ: D.MyProperty указан из рекурсивного вызова. Вы знаете, что уже перечислили E.MyProperty, поэтому это может показаться ненужным, но что, если вы позвоните GetProperties(D)? Вы хотите, чтобы это было опущено?

0 голосов
/ 21 сентября 2010

Каждый из них является как объявлением, так и реализацией (поскольку ни один из них не является абстрактным, поэтому все они определяют тела методов).Ключевые слова override и new - это всего лишь подсказки среде выполнения о том, какую реализацию выбрать, учитывая контекст экземпляра.С помощью отражения вы обходите обычную трассировку наследования и вызываете конкретную реализацию.

Учитывая это, вы все равно можете выяснить, какой из них будет «корневым» вызовом, сделанным из различных точек иерархии.Напомним, что свойства в .NET являются в значительной степени синтаксическим сахаром для определенной структуры методов получения и установки и вспомогательного поля, доступ к которому осуществляется так, как если бы методы доступа были самим полем.Таким образом, PropertyInfo выставляет GetGetMethod() и GetSetMethod(), которые будут возвращать MethodInfo объектов.Вам нужен только один из двух, так как оба будут иметь квалификаторы наследования, присвоенные свойству, но убедитесь, что у вашего свойства есть get или set на всех уровнях иерархии, иначе это взорвется.

Теперь MethodInfo предоставляет несколько свойств, касающихся доступности и наследования.Для вас важна IsHideBySig собственность.Если это возвращает true, тогда этот метод, как объявлено для текущего типа, скрывает идентичную подпись на своем родителе, используя ключевое слово new.Итак, в вашем коде вы хотите посмотреть на участников и проверить две вещи;имеет ли тип базовый тип, отличный от Object (или указанного вами абстрактного типа), и является ли IsHideBySig для метода получения или установки истинным.Если любое из этих значений истинно, это «корневая» реализация для любого типа между текущим типом и следующим наиболее производным типом с аналогичным «корневым».

Итак, ваш foreach в конечном итоге будет выглядетьчто-то вроде этого:

foreach (var item in type.GetProperties(
   BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
      .Where(pi=>pi.GetGetMethod().IsHideBySig || pi.ReflectedType.BaseType == typeof(Object))
{
        Console.WriteLine("{0} {1}", type.Name, item.Name);
}

Теперь помните, что эти «корни» не будут вызываться, если только их дети явно не вызывают их с помощью идентификатора base, и это может произойти независимо от того,текущее свойство скрывает или переопределяет своего родителя.Разница в том, какая реализация будет вызываться средой выполнения, когда экземпляр приводится в качестве одного из его типов-предков.В этом случае выбранная реализация - это реализация самого производного типа в цепочке наследования между приведенными и действительными типами, начиная с приведенного типа, который не скрывает своего родителя.Если самый прямой потомок типа преобразования скрывает своего родителя, реализация типа преобразования используется независимо от того, что эта реализация делает со своим родителем.

В вашей иерархии это будут B, D и E. Если выобъявив новый E и вызвав MyProperty, вы получите реализацию E.Затем, если вы передадите его методу, который принимает любой A и обращается к MyProperty, он будет использовать реализацию B.Если бы вы объявили E и рассматривали его как C, он использовал бы реализацию D.Если вы создали C и рассматривали его как таковой, вы получите реализацию C (потому что C не D), но если вы обработали его как A, вы получите реализацию B.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...