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

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

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

Ответы [ 4 ]

23 голосов
/ 14 ноября 2008

Здесь нам придется иметь дело с методами метода свойства, а не с самим свойством, потому что методы 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, если они оба имели одинаковую сигнатуру и оба были hidebysig - в C # ответ - нет, но если в VB ответ положительный, то это означает, что ответ на этот вопрос фактически недетерминирован).

Ну, я не уверен, насколько все это поможет, кроме как проиллюстрировать, что это на самом деле гораздо более сложная проблема, чем я думал (или я пропустил что-то действительно очевидное, в таком случае мне бы хотелось знать!). Но, надеюсь, у него достаточно контента, чтобы помочь вам достичь того, что вы пытаетесь сделать.

5 голосов
/ 14 ноября 2008

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

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;
}

Не уверен, как это будет работать с индексированными свойствами, однако!

0 голосов
/ 14 ноября 2008

Я никогда не делал то, что вы пытаетесь сделать, но метод MethodInfo.GetBaseDefinition (), кажется, то, что вы ищете.

Возвращает MethodInfo, который этот метод переопределяет.

Из MSDN:

Если данный метод указан с новым ключевым словом (как в новостной ленте, как описано в Типе членов), данный метод возвращается.

0 голосов
/ 14 ноября 2008

Исправление: если вы используете VB, то свойство, которое вы ищете - IsHideBySig. Это будет false в случае, если ключевое слово «new» использовалось для определения метода / свойства.

В случае 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...