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

发布于 2025-01-03 21:29:07 字数 637 浏览 2 评论 0原文

因此,我在我的 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 不保留参数的名称。在没有参数名称的情况下调用它可以按预期工作:

Ping("testOne1")

How do I store a function and call it with命名参数?

So I am hosting IronPython in my C# application. IronPhyton is used to implement a DSL for users. The DSL syntax should be something like this:

Ping(Message = "testOne1")

The hosting code looks like:

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

But this does not work because Action<string> does not keep name of the argument. Calling it without the parameter name works as expected:

Ping("testOne1")

How do I store a function and call it with named arguments?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

风苍溪 2025-01-10 21:29:07

要使用命名参数,您必须静态定义该方法。例如,我将所有 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 代码中。例如,您将拥有一个定义 Python DSL 的 Operations.py 文件,而不是在 C# 中定义“操作”:

# 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。

祝你好运!

To use named arguments you'll have to define the method statically. For example, I'll just put all DSL operations into an Operations static class.

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

Then named arguments will work:

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

Now if I could give you some advise; I'd prefer to implement the actual Python API in Python, and have that call back into my .NET code as needed. For example, instead of having the "operations" defined in C#, you'd have an Operations.py file which defines your Python DSL:

# 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)

And your hosting code doesn't need to change at all.

Both are valid solutions, but the last one lets you iterate on your DSL easily.

Good luck!

沉默的熊 2025-01-10 21:29:07

参数的名称由委托类型中提供的名称定义。对于操作,参数名称为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

The name of the parameter is defined by the name provided in the delegate type. In the case of Action<T>, the parameter name is obj.

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

obj should work for you. Are you sure it isn't working? It works for me.

In an IronPython project I have a library:

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

And this works:

from TestLibrary import Test

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

Hosted, same deal:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文