C# Load IronPython/DLR without dynamic

来源:互联网 发布:软件项目工作报告 编辑:程序博客网 时间:2024/04/29 05:24

【编者按】Ironpython目前分为两个运行库,一个支持.net4.0以下版本,一个支持.net4.0版本。主要的区别就是dynamic的使用。当然能够使用dynamic确实很方便,但是很多情况第三方库还不支持.net4,只能使用2。0、3。5。现在很多C#调用Ironpython的代码都是采用的dynamic方式,在4.0以下版本是运行不了的。还好找到一篇以前介绍Ironpython/DLR的文章,没有使用dynamic,而且介绍还很全面,转载过来。

随着 IronPython 2.0 的发布,.NET Dynamic Language Runtime 总算快成熟了。一直以来,我都期望着用动态脚本以粘合剂的方式编写架构体系中的变化剧烈的逻辑单元,既便于修改,又能灵活适合多变的业务场景。当然,我的目标是在 Platform Framework 中 Embedding Script Engine,而不是用 ipy.exe 去执行一个 "独立" 的 LogicTask。
首先去 CodePlex 下载 IronPython 2.0,然后创建 ConsoleApplication 用于学习测试。还有,别忘了添加引用和 Namespace。

using IronPython.Hosting;

using IronPython.Compiler;

using IronPython.Runtime;

using Microsoft.Scripting;

using System.Runtime.Remoting;

首先看一个简单的例子。

var engine = Python.CreateEngine();

var r = engine.Execute("2 + 3");

Console.WriteLine(r);


很显然,这几行代码打消了我们诸多的顾虑,事情并没有想想中的复杂。
首先,我们需要了解一些基本的 DLR 概念。下图摘自 DLR 帮助文件,通过它我们基本可以了解基于 DLR 的组成方式。

uploads/200812/18_155159_1.png

我们应用的重点是 Common Hosting,至于其他的细节不属于本文关注范围。下图描述了 DLR 的基本执行流程。

uploads/200812/18_155213_2.png

  • ScriptRuntime: 创建 DLR 运行环境,这是整个执行过程的起始点,它表示一个全局的执行状态(比如程序集引用等等)。每个应用程序域(AppDomain)中可以启动多个 ScriptRuntime。
  • ScriptScope: 构建一个执行上下文,其中保存了环境及全局变量。宿主(Host)可以通过创建不同的 ScriptScope 来提供多个数据隔离的执行上下文。
  • ScriptEngine: DLR 动态语言(比如 IronPython) 执行类,可于解析和执行动态语言代码。
  • ScriptSource: 操控动态语言代码的类型,我们可以编译(Compile)、读取(Read Code Lines)或运行(Execute)代码。
  • CompiledCode: 调用 ScriptSource.Compile() 将源代码编译成 CompiledCode,这样多次执行就无需重复编译,从而提高执行性能。
  • ObjectOperations: 提供了相关方法,允许我们在宿主(Host)中操作 DLR 对象成员(Member)。

接下来,我们看一些具体的演示。
1. 执行代码片段

var py = @"

def test():

return 123;


print test();";


var engine = Python.CreateEngine();


var code = engine.CreateScriptSourceFromString(py, SourceCodeKind.Statements);


code.Execute();


输出:
123
注意 Python 对于源代码缩进的格式限制。
2. 使用 ScriptScope 传递数据

var py = @"

x = x + 2;

print x;";

var engine = Python.CreateEngine();

var code = engine.CreateScriptSourceFromString(py, SourceCodeKind.Statements);

var scope = engine.Runtime.Globals; //engine.Runtime.CreateScope();

scope.SetVariable("x", 123);

code.Execute(scope);

var x = scope.GetVariable("x");
onsole.WriteLine(x);


输出:
125
125
可以看出 ScriptScope 可以在 Host 和 ScriptRuntime 间传递数据。
3. 变量共享

public class Data
public int X { get; set; }

}

// -------------------
var py = @"

o.X = o.X + 2;
print o.X;";
var engine = Python.CreateEngine();
var code = engine.CreateScriptSourceFromString(py, SourceCodeKind.Statements);
var scope = engine.Runtime.Globals;
var o = new Data { X = 123 };
scope.SetVariable("o", o);
code.Execute(scope);
Console.WriteLine(o.X);

输出:
125
125
当然,我们还可以使用 MarshalByRefObject 来实现不同 AppDomain 间的对象传递。

ublic class Data  : MarshalByRefObject
{
public int X { get; set; }
}
// --------------------
var py = @"
import clr;
from System import AppDomain;
print AppDomain.CurrentDomain.FriendlyName;
x.X = x.X + 2;
print x.X;";
var engine = Python.CreateEngine(AppDomain.CreateDomain("NewDomain"));
engine.Runtime.LoadAssembly(Assembly.GetAssembly(typeof(int))); // Load Mscorlib.dll Assembly
var code = engine.CreateScriptSourceFromString(py, SourceCodeKind.Statements);
var scope = engine.Runtime.Globals;
var x = new Data { X = 123 };
scope.SetVariable("x", x);
code.Execute(scope);
Console.WriteLine(x.X);

输出:
NewDomain
125
125
4. 载入程序集
我们先创建一个测试类库 test.dll。

namespace My.Library
{
public class MyClass
{
public int Test(int x)
{
return ++x;
} }
}


程序集引用由 ScriptRuntime 进行管理。

var py = @"
import clr;
from My.Library import MyClass;
from System import Console;
o = MyClass();
x.X = o.Test(x.X);
Console.WriteLine(x.X);";

var engine = Python.CreateEngine();
engine.Runtime.LoadAssembly(Assembly.GetAssembly(typeof(int))); // mscorlib.dll

engine.Runtime.LoadAssembly(Assembly.LoadFrom("test.dll")); // test.dll

var code = engine.CreateScriptSourceFromString(py, SourceCodeKind.Statements);

var scope = engine.Runtime.Globals;

var x = new Data { X = 123 };

scope.SetVariable("x", x);code.Execute(scope);



Console.WriteLine(x.X);

出:
124
124
5. 操控 DLR 对象

var py = @"

class Class1:

def __init__(self):

self.i = 100

o = Class1()";

var engine = Python.CreateEngine();

var scope = engine.CreateScope();

var code = engine.CreateScriptSourceFromString(py, SourceCodeKind.Statements);

code.Execute(scope);

var o = scope.GetVariable("o");

var i = engine.Operations.GetMember(o, "i");

Console.WriteLine(i);


输出:
100
6. ScriptRuntime 生命周期

var py = @"
class Class1:
def __init__(self):

self.i = 100
def inc(self):

self.i = self.i + 100;

o = Class1()";

var runtime = Python.CreateRuntime();
var engine = runtime.GetEngine("py");

var scope = engine.CreateScope();
var code = engine.CreateScriptSourceFromString(py, SourceCodeKind.Statements);

// 执行第一段代码
code.Execute(scope);
var o = scope.GetVariableHandle("o");

Console.WriteLine(engine.Operations.GetMember(o, "i"));

// 执行第二段代码

engine.Execute("o.inc()", scope);

Console.WriteLine(engine.Operations.GetMember(o, "i"));

输出:
100
200

 

原帖:http://www.rainsts.net/article.asp?id=764

原创粉丝点击