【转载】使用IronPython给.Net程序加点料

原文地址:https://www.cnblogs.com/pasoraku/p/4906715.html

开发的时候,经常被策划频繁变动的方案而苦恼。这时候就想要加入点动态语言来辅助一下。

在考虑用动态语言之前也曾想过使用动态加载dll的方式,实现基础接口来调用。在卸载的时候遇到了问题,虽可以通过应用程序域来绕过,但这又加入了应用程序域之间的交互。没有动态语言来的方便。

IronPython的官网:http://ironpython.codeplex.com/

在C#中使用IronPython

新建一个项目,ConsoleApplication

然后NuGet添加IronPython包

在Main函数中编写如下代码:

    ScriptEngine engine = Python.CreateEngine();
    ScriptScope scope = engine.CreateScope();
    string script = "print('Hello world!')";
    var sourceCode = engine.CreateScriptSourceFromString(script);
    var result = sourceCode.Execute<object>(scope);
    Console.WriteLine(result);

这里用到了三个主要类型:ScriptEngine, ScriptScope, ScriptSource

顾名思义ScriptEngine是引擎,ScriptScope相当于一个容器可用于传递一些自定义的变量,ScriptSource就是脚本源码。

运行后输出结果:Hello world!

C#向IronPython传递变量

将上面的代码修改如下

复制代码
            ScriptEngine engine = Python.CreateEngine();
            ScriptScope scope = engine.CreateScope();
            string script = "print('Hello %d' %number)";
            scope.SetVariable("number", 123);
            ScriptSource sourceCode = engine.CreateScriptSourceFromString(script);
            var result = sourceCode.Execute<object>(scope);
            Console.WriteLine(result);
复制代码

输出结果变为:Hello 123

还可以试试更奇妙的,比如C#定义一个类

    public class Foo
    {
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
    }

传入这个变量试试,修改Main函数的代码

复制代码
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
string script = @"print('Hello %s' %foo.Name)
foo.DoSth()";//注意这里换行是必须的
Foo foo = new Foo()
{
    Name = "阿萨德",
    Birthday = new DateTime(1999,2,2)
};
scope.SetVariable("foo", foo);
ScriptSource sourceCode = engine.CreateScriptSourceFromString(script);
var result = sourceCode.Execute<object>(scope);
Console.WriteLine(result);
复制代码

成功输出:Hello 阿萨德

那么如果调用Foo里的方法呢?可以哦,你可以试一试,还能够命中断点呢!

执行IronPython文件

将script字符串替换为文件路径,使用ScriptEngine的CreateScriptSourceFromFile方法可以执行文件格式的IronPython

新建文件,名为test.py, 将上面的script字符粘贴到文件内。修改文件的属性为“如果较新则复制”。

那么Main函数的代码段就是:

复制代码
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
string path = @"test.py";
Foo foo = new Foo()
{
    Name = "阿萨德",
    Birthday = new DateTime(1999,2,2)
};
scope.SetVariable("foo", foo);
ScriptSource sourceCode = engine.CreateScriptSourceFromFile(path);
var result = sourceCode.Execute<object>(scope);
Console.WriteLine(result);
复制代码

执行成功,输出结果不变。

但是此时编辑器对py文件的支持是木有的,此时可以安装一个插件,Python Tools for Visual Studio 简称PTVS,可以在GitHub上获取到:https://github.com/Microsoft/PTVS/releases

安装之后,就有了语法高亮和智能提示哦~

这个工具增加了很多对Python的支持,你还能在新建项目中看到新增的Python模板,包括了Django等一些流行的Python网站模板,当然IronPython的模板也是不可少的。

在IronPython中使用C#的类型

还有一个问题,刚才在foo中定义了Birthday这个属性,但是它的类型是DateTime,如何在IronPython中使用它呢?

修改test.py文件中的代码

print('Hello %s' %foo.Name)
foo.DoSth()
from System import DateTime
print("My birthday is %s" %foo.Birthday.ToString())

这里我使用了from System import DateTime这行语句就引入了DateTime的类型

同样的,你也可以引入System程序集中的String、TimeSpan等类型,方便得一塌糊涂,比如这样

from System import *

如果需要添加程序集引用呢?

比如我新建了一个类库,将Foo类放到了这个新的类库中,那么我要使用Foo的时候,只要这样:

复制代码
import clr,sys
clr.AddReference('Foo')
from Foo import Foo

foo = Foo()
foo.Name = "haha"

print('Hello %s' %foo.Name)
from System import *
print("My birthday is %s" %foo.Birthday.ToString())
复制代码

或许你的程序将告诉你一个找不到Module的错误,那就把Foo.dll拷贝到你的执行目录下。或者你也可以修改Main函数中的代码,使用engine.SetSearchPaths(new[]{@"../Foo/bin/Debug"});设置查找类库的路径。

如果报告无法在Foo中找到Foo类型,那就是你拷贝Foo类的代码到类库中去的时候没有使用Foo类库的namespace。

其他

现在已经知道了如何在C#中使用IronPython,以及在IronPython的代码中使用C#的类型和变量传递,那么就可以在你的C#程序中加入炫酷的脚本语言动态特性了。

关于CreateScriptSource的时机,你或许可以使用FileSystemWatcher类来监视文件修改,不过要注意多线程问题哦。

如果你并不需要在C#中加入IronPython,而仅仅只是想要用Python一样的语法来做一些.net的程序比如winform、wpf之类的,你可以安装IronPython的安装包,安装包在最上面给出的官网地址中有下载。安装之后将获得IronPython单独运行的环境和相关的文档。

然后你就可以通过PTVS帮你新增的几个IronPython项目模板来创建你的IronPython程序了。

而且,他们是支持断点调试的哦!你是不是有了好点子呢,比如在C#项目中使用Link文件来链接IronPython项目中的py文件,^_^

我已经迫不及待地想要写一个可以编辑脚本的小游戏来玩玩了呢。

最后加一个在GitHub上看到的IronPython的小游戏Sample

分类: C#.Net学习
标签: IronPython
4
0
« 上一篇: 记一次Redis和NetMQ的测试
» 下一篇: Windows下的UDP爆了10054--远程主机强迫关闭了一个现有的连接
posted @ 2015-10-24 13:48 陈惊蛰 阅读( 6045) 评论( 6) 编辑 收藏

  
#1楼 2015-10-24 14:23 埋头前进的码农  
IronPython好像不维护了吧。
http://pic.cnblogs.com/face/273228/20140809085159.png
  
#2楼 [ 楼主] 2015-10-24 17:23 陈惊蛰  
@ 埋头前进的码农
不会吧,哪儿的新闻
http://pic.cnblogs.com/face/436205/20150522182123.png
  
#3楼 2015-10-27 14:02 林选臣  
@ 陈惊蛰
codeplex上最后一次更新是去年的事情了。玩脚本可以研究下ClearScript,也是微软搞的,基于Google V8和IE的那个啥啥的JS引擎,V8下面效率自然也是不错的。
http://pic.cnblogs.com/face/u352785.png?id=03092151
  
#4楼 2015-12-08 09:59 过错  
@ 林选臣
你说的是2.7吧
https://github.com/IronLanguages/ironpython3 这个一直有更新啊
http://pic.cnblogs.com/face/34113/20160326011719.png
  
#5楼 2016-05-15 16:11 pxczy  
博主大神,请问怎么保存输出的结果
每次 Console.WriteLine(result); 这一句其实是多余的吧
实际上是输出了一个空值,上一句已经输出了

我现在最想保存输出的结果到一个变量,但是找了很久也不知道怎么做
  
#6楼 [ 楼主] 3489701 2016/8/15 16:19:10 2016-08-15 16:19 陈惊蛰  
@ pxczy
Console.WriteLine(result);是用来输出返回值的呀
当然我看了下代码似乎并没有什么返回值
而且也是脚本,不是函数,也不能写return
你说要保存输出结果到一个变量,我没理解,那不是赋值给变量的意思吧?(笑
如果要从脚本返回变量给result,脚本里面写“SystemExit(n)”n就是ScriptSource.Execute<object>(scope);的返回值了
如果你是用C#的变量传参进脚本,哈,执行完之后值就变了,比如:
var mylist= new List<int>{3,1,2};
scope.SetVariable("mylist", mylist);
string script = @"mylist.Sort()
SystemExit(mylist)";
这段执行之后mylist就是1,2,3了。跟函数调用传参没啥区别。

猜你喜欢

转载自blog.csdn.net/lzkqcc/article/details/78725358