Как рефлексия говорит мне, когда свойство скрывает унаследованный элемент с ключевым словом «new»?

StackOverflow https://stackoverflow.com/questions/288357

Вопрос

Так что, если у меня есть:

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top