什么是最适合嵌入C#桌面应用程序的脚本语言?

100

我们正在编写一个复杂的桌面应用程序,需要提供灵活的报告格式,因此我们认为只需将对象模型公开给脚本语言即可。在过去,这意味着使用VBA(仍然是一个选项),但托管代码派生的VSTA似乎已经失去了生命力。

现在,在Windows.NET上嵌入脚本语言的最佳选择是什么?


1
就我个人而言,我开始采用@GrantPeter答案中的方法,使用了一个AppDomain来允许卸载,处理了跨域对象租约续订,并调整了沙盒安全措施。编译后的脚本可以在主程序中调用方法,穿越AppDomain边界。该实验可在此处找到:https://github.com/fadden/DynamicScriptSandbox - fadden
15个回答

118

个人建议使用C#作为脚本语言。.NET框架(包括Mono,感谢Matthew Scharley)实际上在框架内部为每种.NET语言都包含了编译器。

基本上,该系统的实现有两个部分。

  1. 允许用户编译代码 这相对容易,并且可以只用几行代码完成(尽管您可能需要添加一个错误对话框,这可能需要更多几十行代码,具体取决于您希望它有多易用)。

  2. 创建和使用包含在已编译程序集中的类 这比前一步骤要困难一些(需要少量反射)。 基本上,您应该将已编译的程序集视为程序的“插件”。 在C#中有很多教程可以介绍各种创建插件系统的方法(Google是您的朋友)。

我已经实现了一个“快速”应用程序来演示如何实现此系统(包括2个工作脚本!)。 这是应用程序的完整代码,只需创建一个新的应用程序,然后将代码粘贴到“ program.cs”文件中。 此时我必须为即将粘贴的大块代码道歉(我本不想它如此庞大,但在我的评论中有些过度热衷)。


using System;
using System.Windows.Forms;
using System.Reflection;
using System.CodeDom.Compiler;
namespace ScriptingInterface { // 定义脚本接口 public interface IScriptType1 { string RunScript(int value); } }
namespace ScriptingExample { static class Program { /// /// 应用程序的主入口点。 /// [STAThread] static void Main() {
// 编译代码(我很懒,所以我会硬编码它,你肯定可以想出如何从文件/文本框中读取) Assembly compiledScript = CompileCode( "namespace SimpleScripts" + "{" + " public class MyScriptMul5 : ScriptingInterface.IScriptType1" + " {" + " public string RunScript(int value)" + " {" + " return this.ToString() + \" just ran! Result: \" + (value*5).ToString();" + " }" + " }" + " public class MyScriptNegate : ScriptingInterface.IScriptType1" + " {" + " public string RunScript(int value)" + " {" + " return this.ToString() + \" just ran! Result: \" + (-value).ToString();" + " }" + " }" + "}");
if (compiledScript != null) { // 执行脚本 RunScript(compiledScript); } }
// 编译代码 static Assembly CompileCode(string code) { // 创建代码提供程序 // 此类实现“CodeDomProvider”类作为其基础。所有当前的.Net语言(至少Microsoft的)都带有自己的实现, // 因此您可以允许用户使用他们选择的语言(尽管我建议您不要允许使用C++,因为它对于脚本使用来说太不稳定了——任何内存泄漏吗?) Microsoft.CSharp.CSharpCodeProvider csProvider = new Microsoft.CSharp.CSharpCodeProvider();
// 设置选项 CompilerParameters options = new CompilerParameters(); options.GenerateExecutable = false; // 我们需要一个DLL文件(或者在.Net中称为“Class Library”) options.GenerateInMemory = true; // 当我们完成后节省了删除DLL的工作,虽然您可能将其设置为false并节省启动时间,因为下次无需重新编译即可启动 // 并设置您想要的任何其他选项,有相当多的选项,花点时间浏览所有这些选项,并决定哪些最适合您的应用程序!
// 添加您希望用户能够访问的任何引用,但请注意,给他们访问某些类的权限可以允许写入和执行有害代码。 // 我建议你编写自己的类库,只允许它允许的操作,因此他们只能做你想要的事情。 // (虽然像“System.Xml.dll”这样的东西可能很有用,只需要提供一种用户可以读取文件并将其传递给它的方式) // 为了避免太多膨胀在这个例子中,我们将仅向其添加此程序作为其引用,这样我们就不需要另一个 // 项目来存储两者都使用的接口的接口。只要记住,这将公开所有公共类以 // “脚本” options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
// 编译我们的代码 CompilerResults result; result = csProvider.CompileAssemblyFromSource(options, code);
if (result.Errors.HasErrors) { // TODO: 向用户报告脚本出错了 return null; }
if (result.Errors.HasWarnings) { // TODO: 告诉用户警告,可能会

4
供参考,对于任何其他感兴趣的人来说,是的,这个程序可以在Mono上编译和运行得很好。只需要在编译部分添加System引用。 - Matthew Scharley
4
这样做会污染AppDomain。 - Daniel Little
4
如果您不想让它污染您的AppDomain,请创建一个新的AppDomain(这可能是一个好主意,因此您可以在“脚本”上加强更严格的安全性)。 - Grant Peters
4
@Lander - 这非常轻量级。上述代码是一个支持“脚本”的完整程序。csscript.net 看起来像是对此的某种包装器。这基本上是一个最简实现。我认为一个更好的问题应该是“csscript.net 对我有什么作用,而这个代码没有”。我不知道 csscript 在做什么,但他们肯定知道这里的上述代码在做什么,他们在他们的库中使用它(或者极其相似的代码)。 - Grant Peters
2
@selkathguy 是的,命名空间只是为了保持代码干净,并帮助阅读它的人提供清晰度。据我所知,真正限制他们可以访问哪些命名空间的方式并不存在,只能限制他们可以访问哪些程序集。您始终可以使除您想要公开的内容之外的所有内容变为私有/内部(尽管他们仍然可以使用反射来获取它)。如果您想要一个完全安全的环境,您将不得不严格限制他们使用哪些程序集,并将它们置于自己的AppDomain中(可能是每个脚本一个独立的AppDomain,以便脚本无法干扰其他脚本)。 - Grant Peters
显示剩余7条评论

37

24
我曾经使用过CSScript,取得了惊人的结果。在我的脚本应用程序中,它真的减少了绑定和其他低级别的操作。

1
我已经在生产环境中使用CS-Script一年多了,它的表现非常出色。 - David Robbins
顺便提一下 - 要启用C#脚本支持,您可以将C#脚本库集成到内部,或自行进行C#脚本编译。我已经在我的项目中做了类似的轻量级C#脚本编译器,链接如下:https://sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs与其他语言(例如Lua)相比,C#脚本编译本身有点慢,如果不需要额外的编译步骤,最好避免使用。 - TarmoPikaro

20

我认为这是最好的解决方案,因为它不会向AppDomain添加对象。在.Net中,您永远无法卸载程序集或类。 - Daniel Little

12

1
如果这是 https://github.com/boo-lang/boo 上的项目,看起来已经死了。 - kristianp
@kristianp 目前为止,该项目每月有几次提交(最近一个月也是如此)。因此可能已经失去了一些动力,但它仍然似乎是活跃的。 - Tamás Szelei

8
我现在选择的脚本语言是Lua。它小巧、快速、整洁、完全文档化、得到了良好的社区支持,被许多大公司(Adobe、Blizzard、EA Games)在行业中使用,绝对值得一试。
要将其与.NET语言一起使用,LuaInterface项目将提供您所需的一切。

1
Lua也被用于Garry's Mod中的脚本编写,这是一款很棒的游戏 :) - user47322

3

2

如上所述,IronRuby是一个很有意思的项目。对于我这样的C#程序员来说,Mono中的C# Eval支持也是一个非常吸引人的项目。但目前还不可用(将成为Mono 2.2的一部分)。


2

我推荐使用IronPython。它的嵌入非常简单,与.Net类的交互也很直接,而且它本身就是Python。


1
尝试使用Ela。这是一种类似于Haskell的函数式语言,可以嵌入到任何.Net应用程序中。甚至它还有简单但可用的IDE。

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