DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 12.3 Finding Overridden Methods

Problem

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.

Solution

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("\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 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("\tParam {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.

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 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 Also

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

    [ Team LiB ] Previous Section Next Section