2008-11-13 8 views
18

Así que si tengo:¿Cómo me dice la reflexión cuando una propiedad está ocultando un miembro heredado con la palabra clave 'nueva'?

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

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

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

me gustaría que la respuesta sea agnóstico entre C# y vb.net

Respuesta

21

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 que realmente se anulan en lugar de la propiedad en sí misma. 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.

Al observar el IL emitido en varios casos, el método 'get' de la propiedad base tendrá los tokens de metadatos (esto es del compilador C#; otros pueden no emitir el hidebysig según su método que oculte 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 

la derivada tendrá las siguientes fichas:

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

para que podamos ver de esto que no es posible decir puramente de tokens de metadatos del método si es new porque el método base no virtual tiene los mismos símbolos que el método new no virtual, y el método base virtual tiene los mismos símbolos que el método new virtual.

Lo que podemos decir es que si el método tiene la virtual símbolo, pero no el token newslot entonces se anula un método de base en lugar de sombras que, 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, pues, que nos encontramos con el método 'get' no es una anulación, queremos saber si existe una propiedad en la clase base que está sombreado. El problema es que debido a que el método se encuentra en una ranura de tabla de método diferente, en realidad no tiene ninguna relación directa con el método que está sombreando. Entonces, lo que en realidad estamos diciendo es "¿el tipo de base tiene algún método que cumpla con los criterios para sombreado?", Que varía según si el método es hidebysig o hide-by-name.

Para el primero necesitamos verificar si la clase base tiene algún método que coincida con la firma exactamente, mientras que para este último necesitamos verificar si tiene algún método con el mismo nombre, entonces continuando el código de 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 el ocultamiento por nombre ya que C# no lo soporta y eso es prácticamente todo lo que uso, por lo que podría estar equivocado en el código que indica que un método de instancia podría sombrear uno estático. Tampoco sé sobre el problema de sensibilidad (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 hidebysig - en C# la respuesta es no, pero si la respuesta es sí en VB entonces significa que la respuesta a esta pregunta es en realidad no determinista).

Bueno, no estoy seguro de cuanta ayuda es todo esto, aparte de ilustrar que en realidad es un problema mucho más difícil de lo que pensé (o me he perdido algo realmente obvio, en cuyo caso ¡Gustaría saber!). Pero, con suerte, tiene suficiente contenido que te ayuda a lograr lo que intentas hacer.

+0

Gracias por toda la gran información. Voy a experimentar con esto pronto. –

+0

Estoy bastante seguro de que también debe incluir 'BindingFlags.ExactBinding' en su llamada a BaseType.GetMethod(). De lo contrario, informará que 'bool Equals (Foo other)' oculta 'Object.Equals (object)' – RobSiklos

+0

El problema con esta respuesta es que la línea var [prop = typeof (ChildClass) .GetProperty ("TempProperty"); ] lanzará un error si tu tipo es el implementador. Obtendrá un "Encuentro ambiguo encontrado". Y si hace un .GetProperties() en su lugar, notará que la propiedad Shadowed aparecerá dos veces. Entonces, la única forma de hacer que esto funcione es mediante un ciclo posible a través de las propiedades por índice y probarlas y luego realizar un seguimiento de los nombres duplicados y decidir cuál usar. – jrandomuser

-1

corrección, si está utilizando VB la propiedad que busca es "IsHideBySig". Esto será falso en el caso de que la palabra clave "nueva" se usara para definir un método/propiedad.

En el caso C#, ambas instancias se generan como "hidebysig". Gracias por señalar eso, Greg. No me di cuenta de que solo probé esto en VB. Aquí hay un ejemplo de código VB que reproducirá 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 
+0

¿Has probado esto? No es correcto Siempre devuelve verdadero en C#, independientemente de si es virtual, nuevo, reemplazado, etc. –

+0

@Greg - ¿sabes otra? ¿Fuiste tú quien tenía la otra publicación pero la borraste? He leído los documentos y tengo el derecho, esto no es independiente del idioma y también debe devolver "verdadero" y no "falso" para C#. –

0

nunca lo hice lo que está tratando de hacer, pero el método MethodInfo.GetBaseDefinition() parece ser lo que busca.

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

De MSDN:

Si se especifica un método dado con la nueva palabra clave (como en newslot como se describe en Tipo miembros), se devuelve el método dado.

+0

Nota: Esto funcionará para este ejemplo preciso. No funcionará, por ejemplo, si se emite la palabra clave virtual. – JaredPar

3

no parece que la reflexión se diera esto por defecto, así que tendrá que rodar su propia:

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 va a funcionar con propiedades indizadas, sin embargo!

+0

Esto parece prometedor. ¿Qué limitaciones ves con este enfoque? –

Cuestiones relacionadas