98

我们正在编写一个复杂的富桌面应用程序,并且需要在报告格式方面提供灵活性,因此我们认为我们只需将我们的对象模型暴露给脚本语言即可。时间是这意味着 VBA(这仍然是一个选项),但托管代码衍生 VSTA(我认为)似乎已经枯萎了。

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

4

15 回答 15

117

就个人而言,我会使用 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
    {
        /// 
        /// 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
                        }
                    }
                }
            }
        }
    }
}

于 2009-02-27T18:52:19.357 回答
37

铁蟒以下是如何嵌入它的概述。

于 2008-09-26T05:41:28.423 回答
24

我使用CSScript 取得了惊人的效果。它确实减少了在我的可编写脚本的应用程序中进行绑定和其他低级别的工作。

于 2008-09-26T20:41:35.580 回答
20

PowerShell 引擎旨在轻松嵌入到应用程序中,使其可编写脚本。事实上,PowerShell CLI 只是引擎的一个基于文本的界面。

编辑:见https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/

于 2008-09-26T05:52:35.790 回答
12

语言。

于 2008-09-26T05:53:00.187 回答
8

这些天我选择的脚本语言是Lua 。它体积小、速度快、干净、文档齐全、支持良好、有一个很棒的社区,它被业内许多大公司(Adobe、暴雪、EA Games)使用,绝对值得一试。

要将它与 .NET 语言一起使用,LuaInterface项目将提供您所需要的一切。

于 2008-09-26T07:59:29.900 回答
3

为什么不试试 C#?Mono 有一个很棒的新项目,特别是用于动态评估 C#:

http://tirania.org/blog/archive/2008/Sep-10.html

于 2008-09-26T08:10:41.933 回答
2

IronRuby 如上所述。作为一名 C# 程序员,对我来说一个有趣的项目是Mono 中的 C# Eval 支持。但它还不可用(将成为 Mono 2.2 的一部分)。

于 2008-09-26T05:52:58.440 回答
2

IronPython 的另一票。嵌入它很简单,与 .Net 类的互操作也很简单,而且,它是 Python。

于 2008-09-26T20:37:40.240 回答
1

我可能会建议我目前维护的S# 。它是一个开源项目,用 C# 编写,专为 .NET 应用程序设计。

最初(2007-2009 年)它托管在http://www.codeplex.com/scriptdotnet,但最近它被移到了 github。

于 2012-10-30T15:04:55.847 回答
1

试试埃拉。这是一种类似于 Haskell 的函数式语言,可以嵌入到任何 .Net 应用程序中。即使它具有简单但可用的 IDE。

于 2013-11-23T13:04:43.097 回答
0

我还没有尝试过,但它看起来很酷:

http://www.codeplex.com/scriptdotnet

于 2008-09-26T06:03:17.713 回答
0

我刚刚为客户端创建了一个插件,允许他们在模块中编写 C# 代码,就像 VBA 为 Office 所做的那样。

于 2008-09-26T06:06:03.183 回答
0

我以前用过Lua;在 Delphi 应用程序中,但它可以嵌入到很多东西中。它用于Adob​​e 的 Photoshop Lightroom中。

于 2010-04-05T10:49:46.273 回答
0

我喜欢用 C# 本身编写脚本。现在,在 2013 年,对 C# 脚本的支持非常好,越来越多的库可供使用。

Mono 对脚本 C# 代码有很好的支持,您只需Mono.CSharp.dll在应用程序中包含 .NET 即可使用它。对于我制作的 C# 脚本应用程序,请查看CShell

还可以查看来自 Microsoft 的Roslyn中的“ScriptEngine”,但这只是 CTP。

正如一些人已经提到的,CS-Script也已经存在了很长一段时间。

于 2013-06-06T18:42:58.200 回答