在托管应用程序中调用具有命名参数的函数

时间:2012-02-08 16:26:52

标签: c# .net ironpython dynamic-language-runtime

所以我在我的C#应用​​程序中托管IronPython。 IronPhyton用于为用户实现DSL。 DSL语法应该是这样的:

Ping(Message = "testOne1")

托管代码如下:

var engine = Python.CreateEngine();
var scope = engine.CreateScope();

Action<string> ping = (message) => Console.WriteLine(message.ToString());            
scope.SetVariable("Ping", ping);
var script = @"
Ping(Message = ""testOne1"")
";
engine.Execute(script, scope);

但这不起作用,因为Action<string>不保留参数的名称。在没有参数名称的情况下调用它可以按预期工作:

Ping("testOne1")

如何存储函数并使用命名参数调用它?

2 个答案:

答案 0 :(得分:4)

要使用命名参数,您必须静态定义方法。例如,我将把所有DSL操作都放入Operations静态类中。

public static class Operations {
  public static void Ping(string Message) {
    Console.WriteLine(Message);
  }
}

然后命名的参数将起作用:

var engine = Python.CreateEngine();
var scope = engine.CreateScope();

// Load the assembly where the operations are defined.
engine.Runtime.LoadAssembly(Assembly.GetExecutingAssembly());

// Import the operations modules, settings their names as desired.
engine.Execute(@"
from Operations import Ping
", scope);

// Now named arguments will work...
var script = @"
Ping(Message = ""Ping!"")
";

engine.Execute(script, scope);

现在我可以给你一些建议;我更喜欢在Python中实现实际的Python API,并根据需要将其调用回我的.NET代码。例如,您没有在C#中定义“操作”,而是拥有一个定义Python DSL的Operations.py文件:

# Get access to your .NET API
import clr
clr.AddReference("MyAPI")
import MyAPI

# Define the Ping call to call into your .NET API
def Ping(Message):
  MyAPI.Ping(Message)

您的托管代码根本不需要更改。

两者都是有效的解决方案,但最后一个解决方案可让您轻松迭代DSL。

祝你好运!

答案 1 :(得分:0)

参数的名称由委托类型中提供的名称定义。对于Action<T>,参数名称为obj

public delegate void Action<in T>(
    T obj
)

obj应该适合你。你确定它不起作用吗?它对我有用。

在IronPython项目中,我有一个库:

namespace TestLibrary
{
    public static class Test
    {
        public static readonly Action<string> WriteLine =
            msg => Console.WriteLine(msg);

        // the same works if I do this instead
        //public static readonly Action<string> WriteLine = Console.WriteLine;
    }
}

这有效:

from TestLibrary import Test

#Test.WriteLine(msg='foo') # error 
Test.WriteLine(obj='foo') # works

托管,同样的交易:

var engine = Python.CreateEngine();
dynamic scope = engine.CreateScope();

Action<string> writeLine = msg => Console.WriteLine(msg);
// or even
//Action<string> writeLine = Console.WriteLine;
scope.writeLine = writeLine;

//engine.Execute("writeLine(msg='foo')", scope); // error
engine.Execute("writeLine(obj='foo')", scope); // works