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.
Gracias por toda la gran información. Voy a experimentar con esto pronto. –
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
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