Каждый из них является как объявлением, так и реализацией (поскольку ни один из них не является абстрактным, поэтому все они определяют тела методов).Ключевые слова 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.