views:

195

answers:

2

The question I have is this: If I have the MethodInfo object, for a method, obtained from an interface type, and I also have the Type object for a class that implements this interface, but it implements the said method with an explicit implementation, how do I correctly obtain the corresponding MethodInfo object for the implementing method in that class?

The reason I need to do this is that implementing methods can have some attributes applied to them, and I need to find these through reflection, but the class that needs to find those attributes only have an object reference for the implementing class, and the Type object (+ corresponding MethodInfo objects) for the interface.

So, let's assume I have the following program:

using System;
using System.Reflection;

namespace ConsoleApplication8
{
    public interface ITest
    {
        void Test();
    }

    public class Test : ITest
    {
        void ITest.Test()
        {
            throw new NotImplementedException();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Type interfaceType = typeof(ITest);
            Type classType = typeof(Test);

            MethodInfo testMethodViaInterface =
                interfaceType.GetMethods()[0];
            MethodInfo implementingMethod =
                classType.GetMethod(/* ??? */"Test");

            Console.Out.WriteLine("interface: " +
                testMethodViaInterface.Name);
            if (implementingMethod != null)
                Console.Out.WriteLine("class: " +
                    implementingMethod.Name);
            else
                Console.Out.WriteLine("class: unable to locate");

            Console.Out.Write("Press enter to exit...");
            Console.In.ReadLine();
        }
    }
}

Running this gives me:

interface: Test
class: unable to locate
Press enter to exit...

Up in the code there is a .GetMethod call with a ??? comment. This part is what I need help with. Either what I need to specify here (and I've tested a lot, which brings me to the other way) or what I need to replace this code with.

Since I used explicit implementation of the method from the interface, the actual name of the method isn't just "Test". If I dump the entire contents of the GetMethods() array of the class type, with this code:

foreach (var mi in classType.GetMethods(
    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
    Console.Out.WriteLine(mi.Name);
}

then I get this:

ConsoleApplication8.ITest.Test         <-- this is the one I want
ToString
Equals
GetHashCode
GetType
Finalize
MemberwiseClone

clearly, the name has the full name of the interface and its namespace in front of it. However, due to overloading, what it looks like I have to do is find all such implementing methods in the class (ie. assuming there's multiple Test methods varying by parameters), and then compare parameters.

Is there an easier way? Basically I'd like to, once I have the MethodInfo object for a method from an interface, to find the exact method that a class that implements this method, by getting its MethodInfo object.

Note that I'm in a loopy situation here, so if I have to loop through the methods in the class to find the exact method from the interface, that's ok, as long as I have a good way to identify when I have found the right one.

I tried to change the loop above like this:

foreach (var mi in classType.GetMethods(
    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
    if (mi.GetBaseDefinition() == testMethodViaInterface)
        Console.Out.WriteLine(mi.Name);
}

This didn't print out anything, so clearly GetBaseDefinition on such a method doesn't point to the MethodInfo object from the interface.

Any pointers?

+3  A: 

Look at Type.GetInterfaceMap. Sorry - am in a rush so don't have time for a full answer but it should be a start.

Greg Beech
Ok, let me do that.
Lasse V. Karlsen
Ooh, that was exactly what I needed, want to build an extension method on top of it for easy querying, but that hit the spot!
Lasse V. Karlsen
I'll post an answer of my own with the extension method and the edited code.
Lasse V. Karlsen
+1  A: 

For future reference, and if others are interested, the solution given to me by @Greg Beech here was to use Type.GetInterfaceMap.

Here's the altered program code with an extension method at the bottom.

using System;
using System.Linq;
using System.Reflection;
using System.Diagnostics;

namespace ConsoleApplication8
{
    public interface ITest
    {
        void Test();
    }

    public class Test : ITest
    {
        void ITest.Test()
        {
            throw new NotImplementedException();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Type interfaceType = typeof(ITest);
            Type classType = typeof(Test);

            InterfaceMapping map = classType.GetInterfaceMap(interfaceType);

            MethodInfo testMethodViaInterface = interfaceType.GetMethods()[0];
            MethodInfo implementingMethod = testMethodViaInterface.GetImplementingMethod(classType);

            Console.Out.WriteLine("interface: " + testMethodViaInterface.Name);
            if (implementingMethod != null)
                Console.Out.WriteLine("class: " + implementingMethod.Name);
            else
                Console.Out.WriteLine("class: unable to locate"); 

            Console.Out.Write("Press enter to exit...");
            Console.In.ReadLine();
        }
    }

    public static class TypeExtensions
    {
        /// <summary>
        /// Gets the corresponding <see cref="MethodInfo"/> object for
        /// the method in a class that implements a specific method
        /// from an interface.
        /// </summary>
        /// <param name="interfaceMethod">
        /// The <see cref="MethodInfo"/> for the method to locate the
        /// implementation of.</param>
        /// <param name="classType">
        /// The <see cref="Type"/> of the class to find the implementing
        /// method for.
        /// </param>
        /// <returns>
        /// The <see cref="MethodInfo"/> of the method that implements
        /// <paramref name="interfaceMethod"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="interfaceMethod"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="classType"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <para><paramref name="interfaceMethod"/> is not defined in an interface.</para>
        /// </exception>
        public static MethodInfo GetImplementingMethod(this MethodInfo interfaceMethod, Type classType)
        {
            #region Parameter Validation

            if (Object.ReferenceEquals(null, interfaceMethod))
                throw new ArgumentNullException("interfaceMethod");
            if (Object.ReferenceEquals(null, classType))
                throw new ArgumentNullException("classType");
            if (!interfaceMethod.DeclaringType.IsInterface)
                throw new ArgumentException("interfaceMethod", "interfaceMethod is not defined by an interface");

            #endregion

            InterfaceMapping map = classType.GetInterfaceMap(interfaceMethod.DeclaringType);
            MethodInfo result = null;

            for (Int32 index = 0; index < map.InterfaceMethods.Length; index++)
            {
                if (map.InterfaceMethods[index] == interfaceMethod)
                    result = map.TargetMethods[index];
            }

            Debug.Assert(result != null, "Unable to locate MethodInfo for implementing method");

            return result;
        }
    }
}
Lasse V. Karlsen