How can I access an explicitly implemented method

2020-06-04 09:14发布

问题:

Usually, I access a method in reflection like this:

class Foo
{
    public void M () {
        var m = this.GetType ().GetMethod ("M");
        m.Invoke(this, new object[] {}); // notice the pun
    }
}

However, this fails when M is an explicit implementation:

class Foo : SomeBase
{
    void SomeBase.M () {
        var m = this.GetType ().GetMethod ("M");
        m.Invoke(this, new object[] {}); // fails as m is null
    }
}

How do I access an explicitly implemented method using reflection?

回答1:

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);  


回答2:

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);