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

时间:2008-09-26 05:40:02

标签: c# scripting

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

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

15 个答案:

答案 0 :(得分:108)

就个人而言,我会使用C#作为脚本语言。 .NET框架(和Mono,感谢Matthew Scharley)实际上包含了框架本身中每种.NET语言的编译器。

基本上,这个系统的实施有两个部分。

  1. 允许用户编译代码 这是相对容易的,只能在几行代码中完成(尽管您可能想要添加一个错误对话框,这可能是几十行代码,具体取决于您希望它的使用程度)。

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

  3. 我已经实现了一个“快速”应用程序来演示如何实现这个系统(包括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
                            }
                        }
                    }
                }
            }
        }
    }
    
    

答案 1 :(得分:36)

IronPython。以下是how to embed it的概述。

答案 2 :(得分:21)

我使用了CSScript并获得了惊人的效果。它真的减少了必须在我的脚本应用程序中进行绑定和其他低级别的东西。

答案 3 :(得分:19)

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

修改:请参阅https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/

答案 4 :(得分:13)

Boo语言。

答案 5 :(得分:8)

这些天我选择的脚本语言为Lua。它小巧,快速,干净,完整记录,得到很好的支持,有一个很棒的community,它被industry(Adobe,Blizzard,EA Games)中的许多大公司所使用,非常值得一试。< / p>

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

答案 6 :(得分:3)

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

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

答案 7 :(得分:2)

IronRuby如上所述。作为C#程序员,我有一个有趣的项目是C# Eval support in Mono。但它还没有(将成为Mono 2.2的一部分)。

答案 8 :(得分:2)

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

答案 9 :(得分:1)

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

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

答案 10 :(得分:1)

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

答案 11 :(得分:0)

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

http://www.codeplex.com/scriptdotnet

答案 12 :(得分:0)

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

答案 13 :(得分:0)

我之前使用过Lua;在Delphi应用程序中,它可以嵌入很多东西。它在Adobe's Photoshop Lightroom中使用。

答案 14 :(得分:0)

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

Mono对script C# code提供了很好的支持,只需在应用程序中包含Mono.CSharp.dll即可将其用于.NET。对于我已经制作的C#脚本应用程序,请查看CShell

另请查看来自Microsoft的Roslyn中的`ScriptEngine',但这只是CTP。

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