Ответ 1
Нам придется иметь дело с методами здесь, а не с самим свойством, потому что это методы get/set свойства, которые фактически переопределяются, а не сами свойства. Я буду использовать метод get, так как у вас никогда не будет свойства без него, хотя полное решение должно проверить отсутствие его.
Глядя на IL, испущенный в ряде случаев, метод get get базового свойства будет иметь токены метаданных (это из компилятора С#, другие могут не выделять hidebysig
в зависимости от их метода, скрывающего семантику, и в этом случае метод будет скрываться по имени):
non-virtual : .method public hidebysig specialname instance
virtual : .method public hidebysig specialname newslot virtual instance
Полученный код будет иметь следующие токены:
override : .method public hidebysig specialname virtual instance
new : .method public hidebysig specialname instance
new virtual : .method public hidebysig specialname newslot virtual instance
Таким образом, из этого видно, что из токенов метаданных метода невозможно определить, является ли это new
, потому что не виртуальный базовый метод имеет те же токены, что и не виртуальный метод new
и метод виртуальной базы имеет те же токены, что и метод new virtual
.
Мы можем сказать, что если у метода есть токен virtual
, но не токен newslot
, тогда он переопределяет базовый метод, а не тени его, т.е.
var prop = typeof(ChildClass).GetProperty("TempProperty");
var getMethod = prop.GetGetMethod();
if ((getMethod.Attributes & MethodAttributes.Virtual) != 0 &&
(getMethod.Attributes & MethodAttributes.NewSlot) == 0)
{
// the property 'get' method is an override
}
Предполагая, что мы находим, что метод get не является переопределением, мы хотим знать, есть ли свойство в базовом классе, которое оно затеняет. Проблема в том, что, поскольку метод находится в другом слоте таблицы методов, он фактически не имеет прямого отношения к методу, который он скрывает. Так что мы на самом деле говорим: "У базового типа есть какой-либо метод, который соответствует критериям для затенения", который зависит от того, является ли метод hidebysig
или скрытым именем.
Для первого нам нужно проверить, имеет ли базовый класс какой-либо метод, который точно соответствует сигнатуре, тогда как для последнего нам нужно проверить, имеет ли он какой-либо метод с тем же именем, поэтому продолжая код сверху:
else
{
if (getMethod.IsHideBySig)
{
var flags = getMethod.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic;
flags |= getMethod.IsStatic ? BindingFlags.Static : BindingFlags.Instance;
var paramTypes = getMethod.GetParameters().Select(p => p.ParameterType).ToArray();
if (getMethod.DeclaringType.BaseType.GetMethod(getMethod.Name, flags, null, paramTypes, null) != null)
{
// the property 'get' method shadows by signature
}
}
else
{
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
if (getMethod.DeclaringType.BaseType.GetMethods(flags).Any(m => m.Name == getMethod.Name))
{
// the property 'get' method shadows by name
}
}
}
Я думаю, что это большая часть пути, но я все еще не думаю, что это правильно. Для начала я не полностью знаком со скрытием по имени, поскольку С# не поддерживает его, и это почти все, что я использую, поэтому я могу ошибаться в коде здесь, который указывает, что метод экземпляра может затенять статический. Я также не знаю о проблеме чувствительности к регистру (например, в VB метод, называемый Foo
shadow, называется методом Foo
, если оба они имеют одну и ту же подпись и оба были hidebysig
- в С#, ответ - нет, но если ответ да в VB, то это означает, что ответ на этот вопрос фактически недетерминирован).
Ну, я не уверен, насколько это помогает, кроме как проиллюстрировать, что это на самом деле намного сложнее, чем я думал, что это будет (или я пропустил что-то действительно очевидное, в этом случае я бы хотел знать!). Но, надеюсь, он получил достаточный контент, чтобы помочь вам достичь того, что вы пытаетесь сделать.