如何将Python回调传递给C#函数调用

8
我正在尝试使用python.net在mono / ubuntu上调用C#类。目前,我已经成功地使用一个参数进行了简单的函数调用。现在我想做的是将一个python回调函数传递给C#函数调用。我尝试了下面的几种变化,但都没有成功。有人能展示一下如何使其工作吗?
// C# - testlib.cs
class MC {
    public double method1(int n) {
        Console.WriteLine("Executing method1" );
        /* .. */
    }

    public double method2(Delegate f) {
        Console.WriteLine("Executing method2" );
        /* ... do f() at some point ... */
        /* also tried f.DynamicInvoke() */
        Console.WriteLine("Done executing method2" );
    }
}

Python脚本

import testlib, System
mc = testlib.MC()
mc.method1(10) # that works

def f():
    print "Executing f"

mc.method2(f)  
# does not know of method2 with that signature, fair enough...

# is this the right way to turn it into a callback?
f2 = System.AssemblyLoad(f) 
# no error message, but f does not seem to be invoked
mc.method2(f2)              

你尝试过简单地执行 mc.method2(f) 吗? - bosnjak
2个回答

7

尝试传递ActionFunc而不是仅仅原始函数:

我在这里使用了IronPython(因为目前我的机器上没有安装任何mono,但根据Python.NET 文档,我认为它应该可以工作。 实际上,您的代码几乎可以,但您需要导入ActionFunc委托,具体取决于您所需的内容。

Python代码:

import clr
from types import *
from System import Action
clr.AddReferenceToFileAndPath(r"YourPath\TestLib.dll")
import TestLib
print("Hello")
mc = TestLib.MC()
print(mc.method1(10))

def f(fakeparam):
    print "exec f" 

mc.method2(Action[int](f))

这是一个控制台输出:

Hello
Executing method1
42.0
Executing method2
exec f
Done executing method2

C# 代码:

using System;


namespace TestLib
{
    public class MC
    {
        public double method1(int n)
        {
            Console.WriteLine("Executing method1");
            return 42.0;
            /* .. */
        }

        public double method2(Delegate f)
        {
            Console.WriteLine("Executing method2");
            object[] paramToPass = new object[1];
            paramToPass[0] = new int();
            f.DynamicInvoke(paramToPass);
            Console.WriteLine("Done executing method2");
            return 24.0;

        }
    }
}

我再次阅读了有关Python.net 使用泛型 的文档,还发现了这个Python.NET泛型类型的命名和解析,看起来你需要明确指定参数类型。

引用自该文:

(反射)泛型类型定义(如果存在具有给定基本名称的泛型类型定义,并且没有该名称的非泛型类型)。可以使用[]语法将此泛型类型定义绑定为闭合泛型类型。尝试使用()实例化泛型类型def会引发TypeError。


很遗憾,只执行f2 = Action(f)会返回TypeError: cannot instantiate an open generic type。而从System导入Func根本不起作用。 - joelhoro
我再次阅读了Python.net的文档使用泛型,并且还发现了这篇文章Python.NET命名和解析泛型类型,看起来你需要明确指定参数类型。我对我的答案进行了更改。 - Alexander V.
我已经完成了对答案的编辑,请告诉我是否有任何问题。 - Alexander V.
1
谢谢!这个很好用。我唯一的问题是我真的需要一个Func,因为我的C#方法需要使用Python函数的返回值。当我执行var g=f.DynamicInvoke(paramsToPass)时,g似乎是空的。我猜这是一个单独的问题。不过我会授予奖励! - joelhoro
1
谢谢!我认为在这种情况下,尝试用Func替换Action,例如Func[int,int]。稍后我也会尝试进一步研究它。 - Alexander V.
1
问题在于 from System import Func 返回错误,所以我甚至无法执行你建议的操作。 - joelhoro

2

看起来你应该明确定义你的代理:

class MC {
    // Define a delegate type
    public delegate void Callback();

    public double method2(Callback f) {
        Console.WriteLine("Executing method2" );
        /* ... do f() at some point ... */
        /* also tried f.DynamicInvoke() */
        Console.WriteLine("Done executing method2" );
    }
}

然后从 Python 代码中(这只是根据文档的粗略猜测):

def f():
    print "Executing f"

# instantiate a delegate
f2 = testlib.MC.Callback(f)

# use it
mc.method2(f2)

我真的不太明白你的回答。C#类中的第一行没有意义。然后你添加了“Callback f”作为参数,当Callback是一个字段而不是类型时?最后,你尝试通过调用testlib.MC.Callback来设置Callback,就好像它是一个fn。这些都不起作用。我尝试创建一个委托成员,并通过成员fn设置它,但那并没有起作用。显然,f作为它所传递给C#的委托没有被识别。 - joelhoro
抱歉,我没有运行我的C#代码,也从未使用过Python.net,但我认为你定义回调的方式存在问题。在我的示例中,我修复了第一行,实际上是定义了一个委托类型(而不是字段)。这可以通过testlib.MC.Callback暴露给你的Python程序。这基于文档中的示例,建议你实例化EventHandler/Delegate类型的实例(在文档示例中,它是类型为AssemblyLoadEventHandler的事件处理程序)。 - ysalmi
谢谢。顺便说一句,我尝试了一个 Func<double,double> 而不是 delegate,但是我遇到了缺少 System.Func 的错误... 我需要继续尝试。 - joelhoro
顺便问一下,你尝试过IronPython还是一直使用Python.NET?另外,看看这个答案:https://dev59.com/YlHTa4cB1Zd3GeqPPjdF - ysalmi
我可以尝试使用IronPython,但我正在使用Ubuntu,这似乎并不简单。不过我可以试一试。 - joelhoro
成功了!我使用的是SuSE Linux 43.3,mono 4.6.1,python 3.4,pythonnew 2.3.0。一些相关注意事项:我必须使用clr.FindAssebmly()和clr.AddReference(),否则Python将无法导入任何内容(警告:文件路径不能包含符号链接)。我还必须将类封装在命名空间中,然后导入该命名空间。类和方法必须声明为public,使用public关键字。 - cyberthanasis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接