You have an inheritance hierarchy that is several levels deep and has many virtual and overridden methods. You need to determine which method in a derived class overrides what method in one of many possible base classes.
Use the
MethodInfo.GetBaseDefinition
method to determine
which method is overridden in what base class. The following
overloaded method, FindMethodOverrides
, examines
all of the static and public instance methods in a class and displays
which methods override their respective base class methods. This
method also determines which base class the overridden method is in.
This overloaded method accepts an assembly path and name along with a
type name in which to find overriding methods. Note that the
typeName
parameter must be the fully
qualified type name (i.e., the complete namespace hierarchy, followed
by any containing classes, followed by the type name you are
querying):
public void FindMethodOverrides(string asmPath, string typeName) { Assembly asm = Assembly.LoadFrom(asmPath); Type asmType = asm.GetType(typeName); Console.WriteLine("---[" + asmType.FullName + "]---"); // get the methods that match this type MethodInfo[] methods = asmType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); foreach (MethodInfo method in methods) { Console.WriteLine("Current Method: " + method.ToString( )); // get the base method MethodInfo baseDef = method.GetBaseDefinition( ); if (baseDef != method) { Console.WriteLine("Base Type FullName: " + baseDef.DeclaringType.FullName); Console.WriteLine("Base Method: " + baseDef.ToString( )); // list the types of this method Type[] paramTypes = new Type[method.GetParameters( ).Length]; int counter = 0; foreach (ParameterInfo param in method.GetParameters( )) { paramTypes[counter] = param.ParameterType; Console.WriteLine(" Param {0}: {1}", param.Name,param.ParameterType.ToString( )); counter++; } } Console.WriteLine( ); } }
The second overloaded method allows you to determine whether a
particular method overrides a method in its base class. It accepts
the same two arguments as the first overloaded method, along with the
full method name and an array of Type
objects
representing its parameter types:
public void FindMethodOverrides(string asmPath, string typeName, string methodName, Type[] paramTypes) { Console.WriteLine("For [Type] Method: [" + typeName + "] " + methodName); Assembly asm = Assembly.LoadFrom(asmPath); Type asmType = null; asmType = asm.GetType(typeName,true,true); MethodInfo method = asmType.GetMethod(methodName, paramTypes); if (method != null) { MethodInfo baseDef = method.GetBaseDefinition( ); if (baseDef != method) { Console.WriteLine("Base Type FullName: " + baseDef.DeclaringType.FullName); Console.WriteLine("Base Method: " + baseDef.ToString( )); // get the parameters for the base method Type[] baseParamTypes = new Type[baseDef.GetParameters( ).Length]; bool foundMatch = true; // same number of params as we are looking for? if(paramTypes.Length == baseParamTypes.Length) { int counter = 0; foreach (ParameterInfo param in baseDef.GetParameters( )) { if(paramTypes[counter].UnderlyingSystemType != param.ParameterType.UnderlyingSystemType) { // found an unmatching parameter, mark false foundMatch = false; } // list the params so we can see which one we got Console.WriteLine(" Param {0}: {1}", param.Name,param.ParameterType.ToString( )); counter++; } } else foundMatch = false; // we found the one we were looking for if(foundMatch == true) { Console.WriteLine("Found Match!"); } } } Console.WriteLine( ); }
The following code shows how to use each of these overloaded methods:
public static void FindOverriddenMethods( ) { MethodOverrides mo = new MethodOverrides( ); Process current = Process.GetCurrentProcess( ); // get the path of the current module string path = current.MainModule.FileName; // try the easier one mo.FindMethodOverrides(path,"CSharpRecipes.Reflection+DerivedOverrides"); // try the signature findmethodoverrides mo.FindMethodOverrides(path, "CSharpRecipes.Reflection+DerivedOverrides", "Foo", new Type[3] {typeof(long), typeof(double), typeof(byte[])}); }
In the usage code, we are getting the path to the test code assembly
(CSharpRecipes.exe
) via the
Process
class and then using that to find a class
that has been defined in the Reflection
class,
called DerivedOverrides
.
DerivedOverrides
derives from
BaseOverrides
and they are both shown here:
public abstract class BaseOverrides { public abstract void Foo(string str, int i); public abstract void Foo(long l, double d, byte[] bytes); } public class DerivedOverrides : BaseOverrides { public override void Foo(string str, int i) { } public override void Foo(long l, double d, byte[] bytes) { } }
The first method only passes in the assembly path and the fully
qualified type name. This method returns every overridden method for
each method that it finds in the
Reflection.DerivedOverrides
type. If you wanted to
display all overriding methods and their corresponding overridden
method, you can remove the
BindingFlags.DeclaredOnly
binding enumeration from
the
GetMethods
method
call:
MethodInfo[] methods = asmType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
The second method passes in the assembly path, the fully qualified
type name, a method name, and the parameters for this method to find
the override that specifically matches the signature based on the
parameters. In this case, the parameter types of method
Foo
are a long
,
double
, and byte[]
. This method
displays the method that
CSharpRecipes.Reflection+DerivedOverrides.Foo
overrides. The + in the type name represents a nested class.
Determining which
methods override their base class methods would be a tedious chore if
it were not for the GetBaseDefinition
method of
the System.Reflection.MethodInfo
type. This method
takes no parameters and returns a MethodInfo
object that corresponds to the overridden method in the base class.
If this method is used on a MethodInfo
object
representing a method that is not being overridden—as is the
case with a virtual or abstract
method—GetBaseDefinition
returns the
original MethodInfo
object.
The code for the FindMethodOverrides
methods first
loads the assembly using the asmPath
parameter and then gets the type that is specified by the
typeName
parameter.
Once the
type is located, its Type
object’s GetMethod
or
GetMethods
method is called.
GetMethod
is used when both the method name and
its parameter array are passed in to
FindMethodOverrides
; otherwise,
GetMethods
is used. If the method is correctly
located and its MethodInfo
object obtained, the
GetBaseDefinition
method is called on that
MethodInfo
object to get the first overridden
method in the nearest base class in the inheritance hierarchy. This
MethodInfo
type is compared to the
MethodInfo
type that the
GetBaseDefinition
method was called on. If these
two objects are the same, it means that there were no overridden
methods in any base classes; therefore, nothing is displayed. This
code will display only the overridden method; if no methods are
overridden, then nothing is displayed.
See Recipe 12.11; see the “Process Class,” “Assembly Class,” “MethodInfo Class,” and “ParameterInfo Class” topics in the MSDN documentation.