Вызов частного члена базового класса с ImpromptuInterface

У меня есть структура, которая позволяет мне получить доступ к состоянию и методам объектов в моем проекте с клавиатуры. Он сильно зависит от ImpromptuInterface, который великолепен, быстр, гибок и проч.

Например, я вызываю методы с Impromptu.InvokeMember(myObject, methodName, castParameters), Это отлично работает для публичных и частных членов, но когда я пытаюсь позвонить частному myObject базовый класс, я получаю Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'MyType.MyMethod(Something)' is inaccessible due to its protection level,

Самый простой код, который выявляет проблему:

public class MainClass
{
    public static void Main(string[] args)
    {
        var type = new PublicType();
        var other = new OtherType();
        Console.WriteLine(Impromptu.InvokeMember(other, "Method", 2)); //works
        Console.WriteLine(Impromptu.InvokeMember(type, "Method", 2)); //crash
    }
}

public class PublicType : OtherType
{}

public class OtherType
{
    private bool Method(object a)
    {
        return a != null;
    }
}

Я понимаю, почему существует такая проблема, и я вижу некоторые возможные решения, такие как поиск класса, где метод определен, и попытка привести мой объект к этому классу, но это довольно проблематично.

Есть ли какое-нибудь простое решение, желательно строго основанное на экспромте?

1 ответ

Решение

Таким образом, он работает с DLR так, что вы предоставляете вызову контекст Type так что он может определить, какие методы доступны. По умолчанию impromptu использует тип объекта, который вы вызываете, поэтому он обычно работает с большинством закрытых методов, но, очевидно, не с базовыми классами.

В вашем случае вам нужно создать собственный контекст для экспромта, который упоминается в документации UsagePrivate, он работает как для поздних типов привязки, так и для интерфейсов. Также не ясно из документации, но дело в том, что вы можете передать объект typeof() для контекста. Итак, в вашем примере вы можете сделать:

var context = InvokeContext.CreateContext;

Console.WriteLine(Impromptu.InvokeMember(context(type, typeof(OtherType)), "Method", 2));

Если вам нужно сделать это для общих случаев, это не красиво, но вы всегда можете перехватить исключение и рекурсивно попробовать базовый тип, поскольку в общем случае, когда он работает в первый раз, не должно быть замедления, и класс иерархии, как правило, не очень глубоки, и, поскольку вы просто делаете это интерактивно один раз, а не тысячи раз, все будет в порядке.

var context = InvokeContext.CreateContext;
var type = target.GetType()
while(true){
   try{
      Console.WriteLine(Impromptu.InvokeMember(context(target, type), "Method", 2));  
      break;
   }catch(RuntimeBinderException ex){
       type = type.BaseType;
       if(type ==null)
          throw ex;
   }
}
Другие вопросы по тегам