构建Python脚本并在C#中调用方法

10 投票
2 回答
9252 浏览
提问于 2025-04-15 18:29

有没有办法让这个场景正常工作呢?

这里有一个Python脚本。通过用IronPython运行这个脚本,它被打包成了一个DLL文件:

import clr
clr.CompileModules("CompiledScript.dll", "script.py")

我们的目标是从C#代码中调用这个DLL里的方法。.NET Reflector显示这个DLL里有一个类 - DLRCashedCode,而我们感兴趣的方法是这个类里的私有静态方法。

比如,脚本里有一个函数:

def scriptMethod(self, text):
...

在DLL里的表现是:

private static object scriptMethod(Closure closure1, PythonFunction $function, object self, object text)
{
...
}

ClosurePythonFunction是IronPython的类(来自Microsoft.Scripting.dll和IronPython.dll)。

到目前为止,一切都很好。这个方法能被C#代码调用吗?使用反射的方法像是

Type t = typeof(DLRCachedCode);

string methodName = "scriptMethod";
MethodInfo method = t.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);

object[] parameters = new object[] { "param1", "param2" };  // the "params problem"
method.Invoke(null, parameters);

似乎更难,因为要设置方法的参数。如果这些参数(不管怎么说)正确初始化了,我们能期待这个方法顺利运行吗?

有没有更好的方法从C#调用这些方法?由于各种原因,我们更希望将脚本打包成一个.NET程序集,而不是直接调用脚本本身。

2 个回答

6

clr.CompileModules 只是一个加载时的优化,它并不能让脚本像 C# 这样的静态语言那样直接使用。你需要先运行 IronPython 的环境,然后才能把 DLL 文件加载到这个环境中,接着就可以通过 IronPython 提供的接口来使用它。

8

大致上是这样。你不能直接从C#代码访问Python的方法。除非你在使用C# 4.0,并且使用了动态关键字,或者你有特别的需求;)。不过,你可以把一个IronPython类编译成DLL,然后在C#中使用IronPython来访问这些方法(这是针对IronPython 2.6和.NET 2.0的)。

你可以创建一个这样的C#程序:

using System;
using System.IO;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
// we get access to Action and Func on .Net 2.0 through Microsoft.Scripting.Utils
using Microsoft.Scripting.Utils;


namespace TestCallIronPython
{
    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            ScriptEngine pyEngine = Python.CreateEngine();

            Assembly myclass = Assembly.LoadFile(Path.GetFullPath("MyClass.dll"));
            pyEngine.Runtime.LoadAssembly(myclass);
            ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");

            // Get the Python Class
            object MyClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));

            // Invoke a method of the class
            pyEngine.Operations.InvokeMember(MyClass, "somemethod", new object[0]);

            // create a callable function to 'somemethod'
            Action SomeMethod2 = pyEngine.Operations.GetMember<Action>(MyClass, "somemethod");
            SomeMethod2();

            // create a callable function to 'isodd'
            Func<int, bool> IsOdd = pyEngine.Operations.GetMember<Func<int, bool>>(MyClass, "isodd");
            Console.WriteLine(IsOdd(1).ToString());
            Console.WriteLine(IsOdd(2).ToString());

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

然后,写一个简单的Python类,像这样:

class MyClass:
    def __init__(self):
        print "I'm in a compiled class (I hope)"

    def somemethod(self):
        print "in some method"

    def isodd(self, n):
        return 1 == n % 2

编译它(我使用的是SharpDevelop),不过使用clr.CompileModules方法也可以。接着把编译好的MyClass.dll放到编译好的C#程序所在的目录里,然后运行它。你应该会得到这样的结果:

Hello World!
I'm in a compiled class (I hope)
in some method
in some method
True
False
Press any key to continue . . .

这个方法结合了Jeff的更直接的解决方案,省去了创建和编译一个小Python“桩程序”的步骤,同时也展示了如何创建C#函数调用来访问Python类中的方法。

撰写回答