¿Cómo me dice la reflexión cuando una propiedad oculta a un miembro heredado con la palabra clave 'nuevo'?

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

Pregunta

Entonces, si tengo:

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

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

¿Cómo puedo usar la reflexión para ver que ChildClass está ocultando la implementación Base de TempProperty?

Me gustaría que la respuesta fuera agnóstica entre c # y vb.net

¿Fue útil?

Solución

Tendremos que tratar en términos de los métodos de la propiedad aquí en lugar de la propiedad en sí, porque son los métodos get / set de la propiedad los que realmente se anulan en lugar de la propiedad en sí. Usaré el método get ya que nunca debería tener una propiedad sin una, aunque una solución completa debería verificar la falta de una.

Mirando la IL emitida en varios casos, el método 'get' de la propiedad base tendrá los tokens de metadatos (esto es del compilador de C #; otros pueden no emitir el hidebysig dependiendo sobre su método para ocultar la semántica, en cuyo caso el método sería ocultar por nombre):

non-virtual : .method public hidebysig specialname instance
virtual     : .method public hidebysig specialname newslot virtual instance 

El derivado tendrá los siguientes tokens:

override    : .method public hidebysig specialname virtual instance 
new         : .method public hidebysig specialname instance
new virtual : .method public hidebysig specialname newslot virtual instance 

Entonces podemos ver a partir de esto que no es posible distinguir únicamente de los tokens de metadatos del método si es new porque el método base no virtual tiene los mismos tokens que el no virtual new , y el método base virtual tiene los mismos tokens que el método new .

Lo que podemos decir es que si el método tiene el token virtual pero no el token newslot , entonces anula un método base en lugar de lo sombrea, es decir

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
}

Suponiendo, entonces, que encontramos que el método 'get' no es una anulación, queremos saber si hay una propiedad en la clase base que esté sombreando. El problema es que debido a que el método está en una ranura de tabla de métodos diferente, en realidad no tiene ninguna relación directa con el método que está siguiendo. Entonces, lo que en realidad estamos diciendo es "el tipo base tiene algún método que cumpla con los criterios para sombrear", que varía dependiendo de si el método es hidebysig u hide-by-name.

Para el primero, debemos verificar si la clase base tiene algún método que coincida exactamente con la firma, mientras que para el último debemos verificar si tiene algún método con el mismo nombre, por lo que continuamos el código desde arriba:

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

Creo que esto es la mayor parte del camino, pero todavía no creo que sea exactamente correcto. Para empezar, no estoy totalmente familiarizado con la ocultación por nombre, ya que C # no lo admite y eso es todo lo que uso, por lo que puedo estar equivocado en el código aquí que indica que un método de instancia podría sombrear uno estático. Tampoco sé sobre el problema de mayúsculas y minúsculas (por ejemplo, en VB podría un método llamado Foo sombrear un método llamado foo si ambos tenían la misma firma y ambos eran < code> hidebysig : en C # la respuesta es no, pero si la respuesta es sí en VB, significa que la respuesta a esta pregunta no es realmente determinista).

Bueno, no estoy seguro de cuánta ayuda es todo esto, aparte de ilustrar que en realidad es un problema mucho más difícil de lo que pensé que sería (o me he perdido algo realmente obvio en cuyo caso me gustaría ¡saber!). Pero es de esperar que tenga suficiente contenido que lo ayude a lograr lo que está tratando de hacer.

Otros consejos

No parece que la reflexión le dé esto de forma predeterminada, por lo que tendrá que rodar el suyo:

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

¡No estoy seguro de cómo funcionará esto con las propiedades indexadas!

Nunca hice lo que intentas hacer, pero el método MethodInfo.GetBaseDefinition () parece ser lo que estás buscando.

Devuelve el MethodInfo que este método está anulando.

Desde MSDN:

  

Si se especifica un método dado con la nueva palabra clave (como en el boletín de noticias como se describe en Miembros de tipo), se devuelve el método dado.

Corrección, si está utilizando VB, la propiedad que está buscando es '' IsHideBySig ''. Esto será falso en el caso de que el "nuevo" La palabra clave se utilizó para definir un método / propiedad.

En el caso de C #, ambas instancias se muestran como " hidebysig " ;. Gracias por señalar eso Greg. No me di cuenta de que solo probé esto en VB. Aquí hay un código VB de muestra que reprobará este comportamiento.

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top