嵌入C#桌面应用程序中的最佳脚本语言是什么?
我们正在编写一个复杂的富桌面应用程序,需要在报告格式方面提供灵活性,因此我们认为我们只是将对象模型暴露给脚本语言。时间是那个时候意味着VBA(仍然是一种选择),但是托管代码派生的VSTA(我认为)似乎已经枯萎了。
Windows .NET上的嵌入式脚本语言的最佳选择是什么?
解决方案
IronPython。这里是如何嵌入它的概述。
JScript.NET是一个很好的例子。提琴手使用它。
PowerShell引擎被设计为易于嵌入到应用程序中以使其可编写脚本。实际上,PowerShell CLI只是引擎的基于文本的界面。
编辑:请参阅http://blogs.msdn.com/powershell/archive/2007/08/29/making-applications-scriptable-via-powershell.aspx
如上所述的IronRuby。作为C程序员,对我来说一个有趣的项目是Mono中的CEval支持。但是它尚不可用(将成为Mono 2.2的一部分)。
嘘声语言。
我没有尝试过,但是看起来很酷:
http://www.codeplex.com/scriptdotnet
我刚刚为客户端创建了一个插件,允许他们在类似于VBA for Office的模块中编写Ccode。
这些天我选择的脚本语言是Lua。它体积小,速度快,干净,文档齐全,支持良好,拥有一个强大的社区,已被行业中的许多大公司(Adobe,Blizzard,EA Games)使用,绝对值得一试。
要将其与.NET语言一起使用,LuaInterface项目将满足所有需求。
为什么不尝试C#? Mono有一个很棒的新项目,特别是用于动态评估C:
http://tirania.org/blog/archive/2008/Sep-10.html
IronPython的另一票。嵌入起来很简单,与.Net类的互操作也很简单,而且很好,它是Python。
我使用CSScript取得了惊人的效果。它确实减少了在我的可编写脚本的应用程序中进行绑定和其他低级工作的麻烦。
就个人而言,我将使用Cas脚本语言。 .NET框架(以及Mono,感谢Matthew Scharley)实际上在框架本身中包括了每种.NET语言的编译器。
基本上,该系统的实现分为两部分。
- 允许用户编译代码这是相对容易的,并且仅需几行代码即可完成(尽管我们可能想添加一个错误对话框,这可能会增加几十行代码,具体取决于如何使用)我们希望它是)。
- 创建和使用包含在已编译程序集中的类这比上一步要困难一些(需要一点反射)。基本上,我们应该只将编译后的程序集视为程序的"插件"。关于使用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 { /// /// The main entry point for the application. /// [STAThread] static void Main() { // Lets compile some code (I'm lazy, so I'll just hardcode it all, i'm sure you can work out how to read from a file/text box instead 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) { // Create a code provider // This class implements the 'CodeDomProvider' class as its base. All of the current .Net languages (at least Microsoft ones) // come with thier own implemtation, thus you can allow the user to use the language of thier choice (though i recommend that // you don't allow the use of c++, which is too volatile for scripting use - memory leaks anyone?) Microsoft.CSharp.CSharpCodeProvider csProvider = new Microsoft.CSharp.CSharpCodeProvider(); // Setup our options CompilerParameters options = new CompilerParameters(); options.GenerateExecutable = false; // we want a Dll (or "Class Library" as its called in .Net) options.GenerateInMemory = true; // Saves us from deleting the Dll when we are done with it, though you could set this to false and save start-up time by next time by not having to re-compile // And set any others you want, there a quite a few, take some time to look through them all and decide which fit your application best! // Add any references you want the users to be able to access, be warned that giving them access to some classes can allow // harmful code to be written and executed. I recommend that you write your own Class library that is the only reference it allows // thus they can only do the things you want them to. // (though things like "System.Xml.dll" can be useful, just need to provide a way users can read a file to pass in to it) // Just to avoid bloatin this example to much, we will just add THIS program to its references, that way we don't need another // project to store the interfaces that both this class and the other uses. Just remember, this will expose ALL public classes to // the "script" options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location); // Compile our code CompilerResults result; result = csProvider.CompileAssemblyFromSource(options, code); if (result.Errors.HasErrors) { // TODO: report back to the user that the script has errored return null; } if (result.Errors.HasWarnings) { // TODO: tell the user about the warnings, might want to prompt them if they want to continue // runnning the "script" } return result.CompiledAssembly; } static void RunScript(Assembly script) { // Now that we have a compiled script, lets run them foreach (Type type in script.GetExportedTypes()) { foreach (Type iface in type.GetInterfaces()) { if (iface == typeof(ScriptingInterface.IScriptType1)) { // yay, we found a script interface, lets create it and run it! // Get the constructor for the current type // you can also specify what creation parameter types you want to pass to it, // so you could possibly pass in data it might need, or a class that it can use to query the host application ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes); if (constructor != null && constructor.IsPublic) { // lets be friendly and only do things legitimitely by only using valid constructors // we specified that we wanted a constructor that doesn't take parameters, so don't pass parameters ScriptingInterface.IScriptType1 scriptObject = constructor.Invoke(null) as ScriptingInterface.IScriptType1; if (scriptObject != null) { //Lets run our script and display its results MessageBox.Show(scriptObject.RunScript(50)); } else { // hmmm, for some reason it didn't create the object // this shouldn't happen, as we have been doing checks all along, but we should // inform the user something bad has happened, and possibly request them to send // you the script so you can debug this problem } } else { // and even more friendly and explain that there was no valid constructor // found and thats why this script object wasn't run } } } } } } }
我以前用过Lua;在Delphi应用中,但它可以嵌入很多东西中。它在Adobe的Photoshop Lightroom中使用。