Detectar si un método fue anulado usando Reflection (C #)

Digamos que tengo una clase base TestBase donde defino un método virtual TestMe ()

class TestBase { public virtual bool TestMe() { } } 

Ahora heredo esta clase:

 class Test1 : TestBase { public override bool TestMe() {} } 

Ahora, usando Reflection, necesito encontrar si el método TestMe ha sido anulado en la clase infantil – ¿es posible?

Para qué lo necesito: estoy escribiendo un visualizador de diseñador para el tipo “objeto” para mostrar toda la jerarquía de herencia y también para mostrar qué métodos virtuales se redefinieron en qué nivel.

Dado el tipo Test1 , puede determinar si tiene su propia statement de implementación de TestMe :

 typeof(Test1).GetMethod("TestMe").DeclaringType == typeof(Test1) 

Si la statement proviene de un tipo base, esto evaluará falsa.

Tenga en cuenta que, dado que esto es una statement de prueba, no una verdadera implementación, esto será verdadero si Test1 también es abstracto y TestMe es abstracto, ya que Test1 tendrá su propia statement. Si desea excluir ese caso, agregue && !GetMethod("TestMe").IsAbstract

Como señaló @CiprianBortos, la respuesta aceptada no está completa y dará lugar a un desagradable error en tu código si lo usas tal como está.

Su comentario proporciona la solución mágica GetBaseDefinition() , pero no es necesario comprobar el DeclaringType si desea una comprobación IsOverride propósito IsOverride (que creo que era el objective de esta pregunta), simplemente methodInfo.GetBaseDefinition() != methodInfo .

O, proporcionado como un método de extensión en MethodInfo , creo que esto hará el truco:

 public static class MethodInfoUtil { public static bool IsOverride(this MethodInfo methodInfo) { return (methodInfo.GetBaseDefinition() != methodInfo); } } 

No pude lograr que la solución propuesta por Ken Beckett funcionara. Esto es lo que decidí:

  public static bool IsOverride(MethodInfo m) { return m.GetBaseDefinition().DeclaringType != m.DeclaringType; } 

Hay pruebas en la esencia .

Una solución simple que también funcionará para miembros y propiedades protegidos es la siguiente:

 var isDerived = typeof(Test1 ).GetMember("TestMe", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).Length == 0; 

Este es un resumen de mi respuesta aquí , que a su vez hizo referencia a esta pregunta.

Un método que también funciona en algunos casos no triviales:

 public bool Overrides(MethodInfo baseMethod, Type type) { if(baseMethod==null) throw new ArgumentNullException("baseMethod"); if(type==null) throw new ArgumentNullException("type"); if(!type.IsSubclassOf(baseMethod.ReflectedType)) throw new ArgumentException(string.Format("Type must be subtype of {0}",baseMethod.DeclaringType)); while(type!=baseMethod.ReflectedType) { var methods=type.GetMethods(BindingFlags.Instance| BindingFlags.DeclaredOnly| BindingFlags.Public| BindingFlags.NonPublic); if(methods.Any(m=>m.GetBaseDefinition()==baseMethod)) return true; type=type.BaseType; } return false; } 

Y algunas pruebas feas:

 public bool OverridesObjectEquals(Type type) { var baseMethod=typeof(object).GetMethod("Equals", new Type[]{typeof(object)}); return Overrides(baseMethod,type); } void Main() { (OverridesObjectEquals(typeof(List))==false).Dump(); (OverridesObjectEquals(typeof(string))==true).Dump(); (OverridesObjectEquals(typeof(Hider))==false).Dump(); (OverridesObjectEquals(typeof(HiderOverrider))==false).Dump(); (OverridesObjectEquals(typeof(Overrider))==true).Dump(); (OverridesObjectEquals(typeof(OverriderHider))==true).Dump(); (OverridesObjectEquals(typeof(OverriderNothing))==true).Dump(); } class Hider { public virtual new bool Equals(object o) { throw new NotSupportedException(); } } class HiderOverrider:Hider { public override bool Equals(object o) { throw new NotSupportedException(); } } class Overrider { public override bool Equals(object o) { throw new NotSupportedException(); } } class OverriderHider:Overrider { public new bool Equals(object o) { throw new NotSupportedException(); } } class OverriderNothing:Overrider { } 

De acuerdo con esta respuesta, también podría haber una forma simple de verificar si un método virtual fue anulado sin conocer el tipo base o derivado exacto usando una prueba para el atributo MethodAttributes.NewSlot :

 public static bool HasOverride(this MethodInfo method) { return (method.Attributes & MethodAttributes.Virtual) != 0 && (method.Attributes & MethodAttributes.NewSlot) == 0; } 

Junto con otro método de extensión

 private const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; public static bool HasOverride(this Type type, string name, params Type[] argTypes) { MethodInfo method = type.GetMethod(name, Flags, null, CallingConventions.HasThis, argTypes, new ParameterModifier[0]); return method != null && method.HasOverride(); } 

entonces podrías simplemente llamar

 bool hasOverride = GetType().HasOverride(nameof(MyMethod), typeof(Param1Type), typeof(Param2Type), ...); 

para verificar si MyMethod está anulado en una clase derivada.

Por lo que he probado esto, parecía funcionar bien (en mi máquina ™).

Hay una manera mejor, más segura y más rápida de hacerlo. Esta técnica tiene sentido si su instancia de clase va a tener una vida larga y la comprobación IsOverridden debe realizarse varias veces.

Para resolver este problema, podemos usar un caché y C # delegates, ¡mucho más rápido que la reflexión!

 // Author: Salvatore Previti - 2011. /// We need a delegate type to our method to make this technique works. delegate int MyMethodDelegate(string parameter); /// An enum used to mark cache status for IsOverridden. enum OverriddenCacheStatus { Unknown, NotOverridden, Overridden } public class MyClassBase { /// Cache for IsMyMethodOverridden. private volatile OverriddenCacheStatus pMyMethodOverridden; public MyClassBase() { // Look mom, no overhead in the constructor! } ///  /// Returns true if method MyMethod is overridden; False if not. /// We have an overhead the first time this function is called, but the /// overhead is a lot less than using reflection alone. After the first time /// this function is called, the operation is really fast! Yeah! /// This technique works better if IsMyMethodOverridden() should /// be called several times on the same object. ///  public bool IsMyMethodOverridden() { OverriddenCacheStatus v = this.pMyMethodOverridden; switch (v) { case OverriddenCacheStatus.NotOverridden: return false; // Value is cached! Faaast! case OverriddenCacheStatus.Overridden: return true; // Value is cached! Faaast! } // We must rebuild cache. // We use a delegate: also if this operation allocates a temporary object // it is a lot faster than using reflection! // Due to "limitations" in C# compiler, we need the type of the delegate! MyMethodDelegate md = this.MyMethod; if (md.Method.DeclaringType == typeof(MyClassBase)) { this.pMyMethodOverridden = OverriddenCacheStatus.NotOverridden; return false; } this.pMyMethodOverridden = OverriddenCacheStatus.Overridden; return true; } /// Our overridable method. Can be any kind of visibility. protected virtual int MyMethod(string parameter) { // Default implementation return 1980; } /// Demo function that calls our method and print some stuff. public void DemoMethod() { Console.WriteLine(this.GetType().Name + " result:" + this.MyMethod("x") + " overridden:" + this.IsMyMethodOverridden()); } } public class ClassSecond : MyClassBase { } public class COverridden : MyClassBase { protected override int MyMethod(string parameter) { return 2011; } } class Program { static void Main(string[] args) { MyClassBase a = new MyClassBase(); a.DemoMethod(); a = new ClassSecond(); a.DemoMethod(); a = new COverridden(); a.DemoMethod(); Console.ReadLine(); } } 

Cuando ejecuta este progtwig como una aplicación de consola, se imprimirá:

 MyClassBase result:1980 overridden:False ClassSecond result:1980 overridden:False COverridden result:2011 overridden:True 

Probado con Visual Studio 2010, C # 4.0. Debería funcionar también en versiones anteriores, pero puede ser un poco más lento en C # menor que 3.0 debido a las optimizaciones para los delegates en las nuevas versiones, se apreciarán las pruebas sobre esto 🙂 Sin embargo, será aún más rápido que usar el reflection.