Подтвердить что ты не робот

Как отражается рассказ, когда свойство скрывает унаследованного участника с "новым" ключевым словом?

Итак, если у меня есть:

public class ChildClass : BaseClass
{
    public new virtual string TempProperty { get; set; }
}

public class BaseClass
{
    public virtual string TempProperty { get; set; }
}

Как я могу использовать отражение, чтобы увидеть, что ChildClass скрывает базовую реализацию TempProperty?

Я бы хотел, чтобы ответ был агностиком между С# и vb.net

4b9b3361

Ответ 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, то это означает, что ответ на этот вопрос фактически недетерминирован).

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

Ответ 2

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

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

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

Ответ 3

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

Он возвращает MethodInfo, этот метод переопределяет.

Из MSDN:

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

Ответ 4

Коррекция, если вы используете VB, вы ищете "IsHideBySig". Это будет ложным в случае, если для определения метода/свойства было использовано "новое" ключевое слово.

В случае С# оба экземпляра выводятся как "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