Correct usage of Invoke and GetMember

Mar 29, 2009 at 3:21 PM
Hi!

I am currently working on extending an existing .NET application with scripting capabilities based on IronPython. As long as I use dynamic properties everything works fine. Here is a short code snippet about what DOES work (I use C# 3.0 and the DynamicObject implementation of http://www.gotnet.biz/Blog/post/The-Missing-SystemDynamicDynamicObject-Class.aspx):

[...]
internal class DynamicClass : DynamicObject
{
  public DynamicClass() { }
  public override object GetMember(GetMemberAction info)
  {
    return "You asked for " + info.Name;
  }
}
static void Main(string[] args)
{
  var runtime = ScriptRuntime.CreateFromConfiguration();
  var engine = runtime.GetEngine("py");
  var scope = engine.CreateScope();
  var source = engine.CreateScriptSourceFromString("def MyFunc(x): return x.HelloWorld", SourceCodeKind.Statements);
  source.Execute(scope);
  Func<DynamicClass, string> f = scope.GetVariable<Func<DynamicClass, string>>("MyFunc");
  Console.WriteLine(f(new DynamicClass()));
  Console.ReadKey();
}

However, things DO NOT work out well as soon as I try to implement invoke. See the following lines of code and note the questions marked with '???':

internal class DynamicClass : DynamicObject
{
  public DynamicClass() { }

  public override object Invoke(InvokeAction info, object[] arguments)
  {
    // HOW DO I GET METHOD NAME AND PARAMETERS?
    Console.WriteLine("Inside of Invoke");
    // ??? WHAT SHOULD I RETURN?
    return null;
  }

  public override object GetMember(GetMemberAction info)
  {
    if (info.Name == "MyFunc")
    {
      // ??? WHAT SHOULD I RETURN?
      return this;
    }
    return "You asked for " + info.Name;
  }
}
static void Main(string[] args)
{
  var runtime = ScriptRuntime.CreateFromConfiguration();
  var engine = runtime.GetEngine("py");
  var scope = engine.CreateScope();
  var source = engine.CreateScriptSourceFromString("def MyFunc(x): x.MyFunc(1); return x.HelloWorld", SourceCodeKind.Statements);
  source.Execute(scope);
  Func<dynamicclass, /> f = scope.GetVariable<func<dynamicclass, />>("MyFunc");
  Console.WriteLine(f(new DynamicClass()));
  Console.ReadKey();
}

Does anyone have a working sample about how to implement Invoke? I searched the web for days now without any success. Thank you for your help!

Kind regards,
Rainer

Mar 29, 2009 at 3:54 PM
It sounds like you really want "InvokeMember" instead of "Invoke", but you can also return a delegate from GetMember and that delegate will be callable without actually implementing "Invoke".  Invoke would be used if you wanted to define something that looked like a delegate but was dynamic.  Given a dynamic object "foo", "foo()" triggers "Invoke".
Mar 30, 2009 at 6:12 AM
Hi Curt!

 

Thank you for your advice. Returning the delegate in GetMember works great! For those of you who have a similar problem like me, here is the working code:

internal class DynamicClass : DynamicObject
{
  public DynamicClass() { }

  public delegate void MyFuncDelegate( int i );

  public void MyFunc(int i)
  {
    Console.WriteLine("Inside MyFunc: {0}", i);
  }

  public override object GetMember(GetMemberAction info)
  {
    if (info.Name == "MyFunc")
    {
      return new MyFuncDelegate(this.MyFunc);
    }
    return "You asked for " + info.Name;
  }
}

static class Program
{
  static void Main(string[] args)
  {
    var runtime = ScriptRuntime.CreateFromConfiguration();
    var engine = runtime.GetEngine("py");
    var scope = engine.CreateScope();
    var source = engine.CreateScriptSourceFromString("def MyFunc(x): x.MyFunc(10); return x.HelloWorld", SourceCodeKind.Statements);
    source.Execute(scope);
    Func<dynamicclass, /> f = scope.GetVariable<func<dynamicclass, />>("MyFunc");
    Console.WriteLine(f(new DynamicClass()));

    Console.ReadKey();
  }
}