Вызов частного члена базового класса с 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;
}
}