小试牛刀C#作为脚本语言执行解密
背景
我们知道Unity3d是通过C#脚本语言的形式来实现游戏的逻辑代码编写,同样SCOTT服务器也设置了通过C#脚本来实现游戏逻辑,但是本文并不是想真正分析解密他们的运行机制,只是想通过自己的一个需求,来探讨总结下其中的原理。
下面来说下我自己的需求,比较简单,由于经常在非开发环境部署一些小工具,做系统维护,但每次又懒得带笔记本和编译环境到现场,但系统数据又总是那么奇葩,时常有bug出现,突发奇想是否是能把工具做成脚本,这样现场就很容易进行调整(简单修改脚本),现场搞定不用来回折腾,岂不是很Happy。
声明:本文首发于蛮牛,次发博客园,本人原创。
http://www.manew.com/thread-96136-1-1.html
原理
Unity3d使用的Mono这个地球人知道,Scott呢?我以前分析过其源码,大概知道它需要兼容IconPyhton和C#两种脚本,所以它做了一层封装,但是看它引用的DLL lib有Mono的影子,所以估计也是Mono(不对请拍砖,其实它用什么无所谓了)。实际原理很简单了,也就是脚本动态编译(有点像解释执行,严格上应该不是,我觉得李总的热更新的脚本应该不是动态编译的因为很多平台根本不支持比如IOS),查了下资料目前c#动态编译的一共三种方式CodeDom,Mono,Roslyn,这三种教程应该是一堆一堆的,但是Mono和Roslyn的都不是很多,毕竟这个需求比较小众,而且Roslyn是最新出来的,例子都是“Hello world”级别的让人很不爽,最后发现一个库CS-Script,网址是https://csscriptsource.codeplex.com/,看了下文档,瞟一眼代码:
CSScript.EvaluatorConfig.Engine = EvaluatorEngine.Roslyn; //EvaluatorEngine.Mono; //EvaluatorEngine.CodeDom; var sqr = CSScript.Evaluator .CreateDelegate(@"int Sqr(int a) { return a * a; }"); var r = sqr(3);
看到1,2,3行代码的时候,我会心的笑了,实际上CS-Script是对于三种方式的上层封装,可以自定义选择用那一种,这个方案对于我的已经够用了。下面就是做个试验,然后实施即可。
试验
详细的步骤教,我不想多说了CS-Script有自己详细的教程。由于本人的需求是使用自己写的代码调用脚本,这里就不讲通过命令执行脚本的东东了。
第一步安装,创建一个控制台项目,然后使用nuget进行CS-Script的安装,在程序管理控制台执行
Install-Package CS-Script
Nuget会自动下载相关的DLL和Example脚本到项目中很简单,结果如下
其中下面上个脚本是自动添加的例子程序,打开看看很简单,这里就不表了
在Main函数中添加如下代码,测试(这里编译竟然发现有错误,可能是缺Roslyn引用,无卵用,直接剔除或者注释)
static void Main(string[] args)
{
HostApp.Test();
}
执行效果如下,具体可以看Test中的方法就是一些如何动态执行类,方法,静态方法,接口等等简单例子吧,对于我们的简单需求已经够用了(如果有复杂需求比如Host,上下文,高级动态编译什么的请看文档,这里估计也讲不下)
至此简单的测试环境已经有了
实施
根据官方的例子稍微做一下改造,修改Main函数如下:
在bin下写一个自己的Hello.cs脚本:
创建一个批处理执行,以便把文件名称作为动态参数传入,执行效果如下
这里需要注意的是脚本的文件格式别忘记是utf-8的格式,不然中文会乱码的。
应用
上文中的脚本运行模板写基本完成了,这样只要将自己需执行的工具代码,放到单独的类中,然后启动函数为Run即可,这样只要在Dos批处理脚本中修改c#代码文件的相应名称就可以按照脚本执行了,下面是我的工具脚本代码(是一个压缩二级目录的C#源码):
满心欢喜,可结果可耻的失败了
分析其原因主要是我在Myscript脚本中使用了另一个c#类文件中的相关函数,也就是上图中的红色框标注出的代码,实际上这里是一个上下文缺失(或者是host问题),这里的解决办法一种是动态添加Assembly;一种是使用DLL。这里为了简单,我选择第二种方法。这里假设ZipUtil是一种公用类库,我将其封装到独立的DLL中,作为类库引用到工程中去。运行结果和预想的一样执行成功了(So happy)
这里也体现了CS-Script的强大,动态的就管理了第三方应用的DLL竟然不需要做任何处理,赞一个!
总结
至此,小试牛刀使用CS-Script这个工具,很Easy就实现了C#脚本的动态调用和执行,也抛砖引玉引入了C#作为脚本动态编译执行的原理和过程,当然复杂的应用还有很多,比如执行效率,热插拔更新,高级动态编译,上下文这里留给大家自己研究吧。
对本人来说作为自己的需求已经达到,对于动态执行C#脚本也有了更深刻的认识,特别是通过排错的过程,更加深刻的认识到使用脚本的前提和环境是什么:脚本最好是一个单独的C#文件即可(多了就失去了脚本的意义,也没有必要);脚本的使用是在有完善的类库的前提下,通过脚本来实现多变的逻辑,如果类库不够成熟稳定还是不要使用脚本了(这里可以参考下Scott至少它提供了Framework级别的库)
参考阅读: