How can I access an explicitly implemented method using reflection?

后端 未结 2 1335
轮回少年
轮回少年 2021-02-09 13:29

Usually, I access a method in reflection like this:

class Foo
{
    public void M () {
        var m = this.GetType ().GetMethod (\"M\");
        m.Invoke(this,          


        
相关标签:
2条回答
  • 2021-02-09 14:18

    You cannot rely on the name of the method on the implementing class at all - it can be anything. The C# compilers have so far used the convention of prefixing the method name with the fullname of the interface, but that is an internal implementation detail, and also won't be true for e.g. F#. The proper way is by using InterfaceMapping if you want a MethodInfo for the implementation.

    For example if we have the following structure

    namespace LibBar
    {
      [AttributeUsage(AttributeTargets.Method)]
      public class AnswerAttribute : Attribute { }
    
      public interface IFoo
      {
        void Hello();
        int GetAnswer();
        object WhoAmI();
      }
    }
    

    And in an F# project

    namespace LibFoo
    open LibBar
    
    type Foo() = 
        interface IFoo with
            [<Answer>]
            member this.GetAnswer() = 42
            member this.Hello() = printf "Hello, World!"
            member this.WhoAmI() = this :> obj
    

    If we just want to call GetAnswer() through reflection then it suffices to get the MethodInfo for the interface

    Foo obj = new Foo();
    int answer = (int)typeof(IFoo)
      .GetMethod("GetAnswer")
      .Invoke(obj, null);
    

    However say that we want to see if the implementation has the AnswerAttribute. Then it won't be enough to just have the MethodInfo for the method on the interface. The name of the method would be "LibBar.IFoo.GetAnswer" if this had been C#, but we want it to work independent of implementation details in the compiler and language used.

    private static MethodInfo GetMethodImplementation(Type implementationType, MethodInfo ifaceMethod)
    {
      InterfaceMapping ifaceMap = implementationType.GetInterfaceMap(ifaceMethod.DeclaringType);
      for (int i = 0; i < ifaceMap.InterfaceMethods.Length; i++)
      {
        if (ifaceMap.InterfaceMethods[i].Equals(ifaceMethod))
          return ifaceMap.TargetMethods[i];
      }
      throw new Exception("Method missing from interface mapping??"); // We shouldn't get here
    }
    
    ...
    
      Foo obj = new Foo();
      MethodInfo ifaceMethod = typeof(IFoo).GetMethod("GetAnswer");
      MethodInfo implementationMethod = GetMethodImplementation(typeof(Foo), ifaceMethod);
      Console.WriteLine("GetAnswer(): {0}, has AnswerAttribute: {1}",
        implementationMethod.Invoke(obj, null),
        implementationMethod.GetCustomAttribute<AnswerAttribute>() != null);
    
    0 讨论(0)
  • 2021-02-09 14:24

    It's because the name of the method is not "M", it will be "YourNamespace.SomeBase.M". So either you will need to specify that name (along with appropriate BindingFlags), or get the method from the interface type instead.

    So given the following structure:

    namespace SampleApp
    {    
        interface IFoo
        {
            void M();
        }
    
        class Foo : IFoo
        {
            void IFoo.M()
            {
                Console.WriteLine("M");
            }
        }
    }
    

    ...you can do either this:

    Foo obj = new Foo();
    obj.GetType()
        .GetMethod("SampleApp.IFoo.M", BindingFlags.Instance | BindingFlags.NonPublic)
        .Invoke(obj, null);            
    

    ...or this:

    Foo obj = new Foo();
    typeof(IFoo)
        .GetMethod("M")
        .Invoke(obj, null);  
    
    0 讨论(0)
提交回复
热议问题