Как я могу получить доступ к явно реализованному методу, используя отражение? - PullRequest
5 голосов
/ 06 сентября 2010

Обычно я получаю доступ к методу в отражении, как это:

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

Однако, это терпит неудачу, когда M - явная реализация:

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

Как получить доступ к явно реализованному методу, используя отражение?

Ответы [ 2 ]

9 голосов
/ 06 сентября 2010

Это потому, что имя метода не "M", оно будет "YourNamespace.SomeBase.M".Так что либо вам нужно будет указать это имя (вместе с соответствующим BindingFlags), либо вместо этого получить метод из типа интерфейса.

Итак, учитывая следующую структуру:

namespace SampleApp
{    
    interface IFoo
    {
        void M();
    }

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

...вы можете сделать это:

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

... или это:

Foo obj = new Foo();
typeof(IFoo)
    .GetMethod("M")
    .Invoke(obj, null);  
1 голос
/ 10 октября 2018

Вы не можете полагаться на имя метода в реализующем классе - это может быть что угодно. Компиляторы C # до сих пор использовали соглашение о добавлении префикса имени метода к полному имени интерфейса, но это внутренняя деталь реализации, которая также не будет истинна, например, для примера. F #. Правильный способ - использовать InterfaceMapping, если вы хотите MethodInfo для реализации.

Например, если у нас есть следующая структура

namespace LibBar
{
  [AttributeUsage(AttributeTargets.Method)]
  public class AnswerAttribute : Attribute { }

  public interface IFoo
  {
    void Hello();
    int GetAnswer();
    object WhoAmI();
  }
}

И в проекте F #

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

Если мы просто хотим вызвать GetAnswer() через отражение, то достаточно получить MethodInfo для интерфейса

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

Однако скажем, что мы хотим посмотреть, есть ли у реализации атрибут ответа. Тогда не будет достаточно иметь MethodInfo для метода на интерфейсе. Имя метода было бы "LibBar.IFoo.GetAnswer", если бы это был C #, но мы хотим, чтобы он работал независимо от деталей реализации в компиляторе и используемом языке.

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