4

I have a framework that allows me to access the state and methods of objects in my project with keyboard. It relies heavily on ImpromptuInterface, which is great and fast and flexible and stuff.

For example, I invoke methods with Impromptu.InvokeMember(myObject, methodName, castParameters). It worked great for public and private members, but when I try to call a private member of myObject base class, I get Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'MyType.MyMethod(Something)' is inaccessible due to its protection level.

The simplest code that reveals the problem:

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

I understand why there is such a problem and I can see some possible resolutions, like looking for the class, where the method is defined and trying to cast my object to that class, but that's quite troublesome.

Is there any simple solution, preferably based strictly on Impromptu?

4

1 回答 1

2

So the way it works in with the DLR is that you give the invocation a context Type so it can determine what methods are accessible. By default impromptu uses the type of the object you are invoking on so it typically works with most private methods, but obviously not of base classes.

In your case you need to create your own context for impromptu, which is mentioned in the documentation UsagePrivate, it works for the late binding types just as well as the interfaces. Also not clear from the documentation, but is the case, is that you can pass in a typeof() object for the context. So in your example you can do:

var context = InvokeContext.CreateContext;

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

If you have to do this for generic cases, it's not pretty, but you can always catch the exception and recursively try the base type, for the general case of it working on the first time there shouldn't be a slow down, and class hierarchies generally aren't very deep and since you are just doing this interactively once rather than thousands of times it should be okay.

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;
   }
}
于 2012-12-31T17:34:48.730 回答