关于反射:将从接口类型获得的MethodInfo对象转换为C#中实现类型的相应MethodInfo对象?

Translating a MethodInfo object obtained from an interface type, to the corresponding MethodInfo object on an implementing type in C#?

我的问题是:如果我有一个MethodInfo对象,对于一个方法,从一个接口类型中获取,并且我也有一个实现这个接口的类的类型对象,但是它用一个显式的实现来实现这个方法,我如何正确地获得这个实现方法对应的MethodInfo对象呢?班级?

我需要这样做的原因是,实现方法可以应用一些属性,我需要通过反射来找到这些属性,但是需要找到这些属性的类只具有实现类的对象引用,以及接口的类型对象(+对应的MethodInfo对象)。

那么,假设我有以下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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();
        }
    }
}

运行这个可以让我:

1
2
3
interface: Test
class: unable to locate
Press enter to exit...

在代码中有一个.getMethod调用,带有????评论。这部分是我需要帮助的。要么我需要在这里指定什么(我已经测试了很多,这让我想到了另一种方法),要么我需要用什么来替换这个代码。

因为我使用了接口中方法的显式实现,所以方法的实际名称不仅仅是"test"。如果使用以下代码转储类类型的getMethods()数组的全部内容:

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

然后我得到这个:

1
2
3
4
5
6
7
ConsoleApplication8.ITest.Test         <-- this is the one I want
ToString
Equals
GetHashCode
GetType
Finalize
MemberwiseClone

显然,该名称前面有接口及其命名空间的全名。但是,由于重载,我必须在类中找到所有这样的实现方法(即假设有多个测试方法随参数变化),然后比较参数。

有更简单的方法吗?基本上,我希望,一旦我有了接口方法的MethodInfo对象,通过获取其MethodInfo对象,找到实现此方法的类的确切方法。

请注意,这里的情况比较复杂,所以如果我必须循环使用类中的方法从接口中找到准确的方法,这是可以的,只要我有一个很好的方法来识别什么时候找到了正确的方法。

我试着这样改变上面的循环:

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

这并没有打印出任何内容,因此很明显,此类方法上的GetBaseDefinition没有从接口指向MethodInfo对象。

有指针吗?


为了将来参考,如果其他人感兴趣,这里@greg beech提供给我的解决方案是使用type.getinterfacemap。

下面是修改过的程序代码,底部有一个扩展方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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;
        }
    }
}

看看Type.GetInterfaceMap。抱歉-我很忙,所以没有时间完整回答,但这应该是一个开始。