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


98

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

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


FWIW,我从@GrantPeter的答案中的方法开始,使用AppDomain允许卸载,处理跨域对象租约的续订以及沙箱安全性措施。编译后的脚本可以跨AppDomain边界调用主程序中的方法。可在以下位置找到实验:github.com/fadden/DynamicScriptSandbox
渐行渐远的

Answers:


24

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


1
我已经在生产中使用CS-Script一年多了,它的表现非常好。
David Robbins 2010年

顺便说一句-要启用C#脚本支持,您可以在内部集成C#脚本库,也可以自己进行C#脚本编译。我在这里在自己的项目中做了类似的轻量级C#脚本编译器: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs C#脚本编译本身有点慢(例如,与.Lua相比)避免不必要的编译步骤。
TarmoPikaro

114

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


2
您知道这是否也可以在Mono中使用,还是仅在.NET上可用?
马修·沙利

4
仅供参考,对于任何其他好奇的人,是的,它确实可以在Mono上编译并运行。只需要系统参考的编译部分。
马修·沙利

3
这样做会污染AppDomain
Daniel Little

4
@Lavinski如果您不希望它污染您的AppDomain,则只需创建一个新的(无论如何这可能是一个好主意,因此您可以在“脚本”上放置更严格的安全性)
Grant Peters

3
@Lander-这是非常轻巧的。上面的代码是支持“脚本”的整个程序。csscript.net看起来像是某种包装。这基本上是一个准系统的实现。我认为一个更好的问题是“ csscript.net对我有什么作用,而事实并非如此”。我不知道csscript在做什么,但是他们肯定知道上面的代码在做什么,他们在库中有(或非常相似的东西)。
Grant Peters 2014年




8

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

要将其与.NET语言一起使用,LuaInterface项目将满足您的所有需求。


1
Lua还用于Garry's Mod中的脚本编写,这是一个很棒的游戏:)
匿名



2

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


1

我可能会建议我目前维护的S#。这是一个开放源代码项目,用C#编写,专门用于.NET应用程序。

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


感谢您发布答案!请务必仔细阅读有关自我促销常见问题解答。另请注意,每次链接到您自己的站点/产品,您都必须发布免责声明。
安德鲁·巴伯

感谢安德鲁的建议。先前的答案之一包含此脚本语言的过时链接。由于某些原因,我无法在原始答案中添加评论,因此我发布了新的评论以提供正确的链接。
彼得



0

我刚刚为客户端创建了一个插件,从而允许他们在类似于VBA for Office的模块中编写C#代码。



0

我喜欢用C#本身编写脚本。现在,在2013年,已经对C#脚本提供了很好的支持,并且有越来越多的C#脚本库可供使用。

Mono对C#代码脚本提供了强大的支持,您只需Mono.CSharp.dll在应用程序中包含,就可以在.NET中使用它。对于我制作的C#脚本应用程序,请查看CShell

还要检查来自Microsoft的Roslyn中的“ ScriptEngine” ,但这只是CTP。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.