Finding Overridden Methods






Finding Overridden Methods

Problem

You have an inheritance hierarchy that is several levels deep and has many virtual and overridden methods. You need a list of the base class method(s) that are overridden by methods within a derived class.

Solution

Use the MethodInfo.GetBaseDefinition method to determine which method is overridden in what base class. The overloaded FindMethodOverrides method shown in Figure examines all of the 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).

Overloaded FindMethodOverrides methods

public class ReflectionUtils
{
    public static void FindMethodOverrides(string asmPath, string typeName)
    {
        Assembly asm = Assembly.ReflectionOnlyLoadFrom(asmPath);
        Type type = asm.GetType(typeName);
        FindMethodOverrides(type);
    }

    public static void FindMethodOverrides(Type type)
    {
        Console.WriteLine("---[" + type.FullName + "]---");

        // Get the methods defined by this type.
        MethodInfo[] methods = type.GetMethods(BindingFlags.Instance |
            BindingFlags.NonPublic | BindingFlags.Public |
            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("\tParam {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 class ReflectionUtils
	{
	    public static void FindMethodOverrides(string asmPath, string typeName,
	        string methodName, Type[] paramTypes)
	    {
	        Console.WriteLine("For [Type] Method: [" + typeName + "] " + methodName);

	        Assembly asm = Assembly.ReflectionOnlyLoadFrom(asmPath);
	        Type type = asm.GetType(typeName,true,true);
	        MethodInfo method = type.GetMethod(methodName, paramTypes);
	        FindMethodOverrides(method, paramTypes);
	    }
	    public static void FindMethodOverrides(MethodInfo method,
	               Type[] 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( ));
	                foreach (ParameterInfo param in baseDef.GetParameters( ))
	                {
	                    // List the params so we can see which one we got.
	                    Console.WriteLine("\tParam {0}: {1}",
	                        param.Name,param.ParameterType.ToString( ));
	                }
	            // We found the one we were looking for.
	            Console.WriteLine("Found Match!");
	        }
	        Console.WriteLine( );
	    }
	}

The following code shows how to use each of these overloaded methods:

	public static void FindOverriddenMethods( )
	{
	    Process current = Process.GetCurrentProcess( );
	    // Get the path of the current module.
	    string path = current.MainModule.FileName;

	    // Try the easier one.
	    ReflectionUtils.FindMethodOverrides.
	        (path,"CSharpRecipes.ReflectionUtils+DerivedOverrides");

	    // Try the signature FindMethodOverrides.
	    ReflectionUtils.FindMethodOverrides(path,
	        "CSharpRecipes.ReflectionUtils+DerivedOverrides",
	        "Foo",
	        new Type[3] {typeof(long), typeof(double), typeof(byte[])});
	}

The output of this method, using the BaseOverrides and DerivedOverrides classes defined afterward, is shown here:

	---[CSharpRecipes.ReflectionUtils+DerivedOverrides]---
	Current Method: Void Foo(System.String, Int32)
	Base Type FullName: CSharpRecipes.ReflectionUtils+BaseOverrides
	Base Method: Void Foo(System.String, Int32)
	        Param str: System.String
	        Param i: System.Int32

	Current Method: Void Foo(Int64, Double, Byte[])
	Base Type FullName: CSharpRecipes.ReflectionUtils+BaseOverrides
	Base Method: Void Foo(Int64, Double, Byte[])
	        Param l: System.Int64
	        Param d: System.Double
	        Param bytes: System.Byte[]

	For [Type] Method: [CSharpRecipes.ReflectionUtils+DerivedOverrides] Foo
	Base Type FullName: CSharpRecipes.ReflectionUtils+BaseOverrides
	Base Method: Void Foo(Int64, Double, Byte[])
	        Param l: System.Int64
	        Param d: System.Double
	        Param bytes: System.Byte[]
	Found Match!

In the usage code, you get the path to the test code assembly (CSharpRecipes.exe) via the Process class. You then use that to find a class that has been defined in the ReflectionUtils 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 passes in only 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 want to display all overriding methods and their corresponding overridden methods, you can remove the BindingFlags. DeclaredOnly binding enumeration from the GetMethods method call:

	MethodInfo[] methods = asmType.GetMethods(BindingFlags.Instance |
	                       BindingFlags.NonPublic | BindingFlags.Public);

This change now produces the following output using the same classes, BaseOverrides and DerivedOverrides:

	---[CSharpRecipes.ReflectionUtils+DerivedOverrides]---
	Current Method: Void Foo(System.String, Int32)
	Base Type FullName: CSharpRecipes.ReflectionUtils+BaseOverrides
	Base Method: Void Foo(System.String, Int32)
	        Param str: System.String
	        Param i: System.Int32

	Current Method: Void Foo(Int64, Double, Byte[])
	Base Type FullName: CSharpRecipes.ReflectionUtils+BaseOverrides
	Base Method: Void Foo(Int64, Double, Byte[])
	        Param l: System.Int64
	        Param d: System.Double
	        Param bytes: System.Byte[]

	Current Method: System.Type GetType()

	Current Method: System.Object MemberwiseClone()

	Current Method: System.String ToString()
	Base Type FullName: System.Object
	Base Method: System.String ToString()

	Current Method: Boolean Equals(System.Object)
	Base Type FullName: System.Object
	Base Method: Boolean Equals(System.Object)
	        Param obj: System.Object

	Current Method: Int32 GetHashCode()
	Base Type FullName: System.Object
	Base Method: Int32 GetHashCode()

	Current Method: Void Finalize()
	Base Type FullName: System.Object
	Base Method: Void Finalize()

	For [Type] Method: [CSharpRecipes.ReflectionUtils+DerivedOverrides] Foo
	Base Type FullName: CSharpRecipes.ReflectionUtils+BaseOverrides
	Base Method: Void Foo(Int64, Double, Byte[])
	        Param l: System.Int64
	        Param d: System.Double
	        Param bytes: System.Byte[]
	Found Match!

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 long, double, and byte[]. This method displays the method that CSharpRecipes.ReflectionUtils+DerivedOverrides.Foo overrides. The + in the type name represents a nested class.

Discussion

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 overriddenas is the case with a virtual or abstract methodGetBaseDefinition 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 methods; if no methods are overridden, then nothing is displayed.

See Also

See Recipe 13.10; see the "Process Class," "Assembly Class," "MethodInfo Class," and "ParameterInfo Class" topics in the MSDN documentation.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows