Как рефлексия говорит мне, когда свойство скрывает унаследованный элемент с ключевым словом «new»?
-
08-07-2019 - |
Вопрос
Так что, если у меня есть:
public class ChildClass : BaseClass
{
public new virtual string TempProperty { get; set; }
}
public class BaseClass
{
public virtual string TempProperty { get; set; }
}
Как я могу использовать отражение, чтобы увидеть, что ChildClass скрывает базовую реализацию TempProperty?
Я бы хотел, чтобы ответ был независимым между c # и vb.net
Решение
Здесь нам придется иметь дело с методами метода свойства, а не с самим свойством, потому что методы get / set свойства на самом деле переопределяются, а не само свойство. Я буду использовать метод get, так как у вас никогда не должно быть свойства без него, хотя полное решение должно проверять его отсутствие.
Глядя на IL, выдаваемый в ряде случаев, метод 'get' базового свойства будет иметь токены метаданных (это от компилятора C #; другие могут не генерировать 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's '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's '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's 'get' method shadows by name
}
}
}
Я думаю, что это большая часть пути, но я все еще не думаю, что это совершенно правильно. Для начала я не совсем знаком с сокрытием по имени, поскольку C # не поддерживает его, и это почти все, что я использую, поэтому я могу ошибаться в коде, который указывает, что метод экземпляра может скрывать статический метод. Я также не знаю о проблеме чувствительности к регистру (например, в VB мог бы метод с именем Foo
затенять метод с именем foo
, если они оба имели одинаковую подпись и оба были < code> hidebysig - в C # ответ отрицательный, но если в VB ответ положительный, то это означает, что ответ на этот вопрос фактически недетерминирован).
Ну, я не уверен, насколько все это поможет, кроме как для иллюстрации того, что это на самом деле гораздо более сложная проблема, чем я думал (или я пропустил что-то действительно очевидное, в таком случае мне бы хотелось знать!). Но, надеюсь, у него достаточно контента, чтобы помочь вам достичь того, что вы пытаетесь сделать.
Другие советы
Похоже, отражение не выдаст вам это по умолчанию, поэтому вам придется свернуть свое собственное:
public static bool IsHidingMember( this PropertyInfo self )
{
Type baseType = self.DeclaringType.BaseType;
PropertyInfo baseProperty = baseType.GetProperty( self.Name, self.PropertyType );
if ( baseProperty == null )
{
return false;
}
if ( baseProperty.DeclaringType == self.DeclaringType )
{
return false;
}
var baseMethodDefinition = baseProperty.GetGetMethod().GetBaseDefinition();
var thisMethodDefinition = self.GetGetMethod().GetBaseDefinition();
return baseMethodDefinition.DeclaringType != thisMethodDefinition.DeclaringType;
}
Однако не уверен, как это будет работать с индексированными свойствами!
Я никогда не делал то, что вы пытаетесь сделать, но метод MethodInfo.GetBaseDefinition (), кажется, то, что вы ищете.
Возвращает MethodInfo, который этот метод переопределяет.
Из MSDN:
Если заданный метод указан с новым ключевым словом (как в новостной ленте, как описано в Типе членов), данный метод возвращается.
Исправление: если вы используете VB, свойство, которое вы ищете, является " IsHideBySig " ;. Это будет ложным в случае, если " новый " ключевое слово использовалось для определения метода / свойства.
В случае C # оба экземпляра выводятся как " hidebysig " ;. Спасибо за указание на это Грег. Я не осознавал, что проверил это только на VB. Вот пример кода VB, который будет воспроизводить это поведение. Р>
Module Module1
Class Foo
Public Function SomeFunc() As Integer
Return 42
End Function
End Class
Class Bar
Inherits Foo
Public Shadows Function SomeFunc() As Integer
Return 36
End Function
End Class
Sub Main()
Dim type = GetType(Bar)
Dim func = type.GetMethod("SomeFunc")
Stop
End Sub
End Module