Яку найкращу мову сценаріїв можна вбудувати у настільний додаток C #? [зачинено]


98

Ми пишемо складний багатий настільний додаток і нам потрібно запропонувати гнучкість у форматах звітності, тому ми подумали, що ми просто підведемо нашу об’єктну модель на сценарій сценарію. Час був тоді, коли це означало VBA (що все ще є варіантом), але керована похідна код VSTA (я думаю), здається, вимерла на лозі.

Який зараз найкращий вибір для вбудованої мови сценаріїв у Windows .NET?


FWIW, я почав з підходу у відповіді @ GrantPeter, використовував AppDomain, щоб дозволити вивантаження, обробляв оновлення міждоменного об'єкта оренди та поширювався на заходи безпеки пісочниці. Скомпільований сценарій може викликати методи в основній програмі назад через межу AppDomain. Експеримент можна знайти тут: github.com/fadden/DynamicScriptSandbox
fadden

Відповіді:


24

Я використовував CSScript з дивовижними результатами. Це дійсно скорочується необхідність робити прив'язки та інші речі низького рівня в моїх сценаріях.


1
Я використовую CS-Script у виробництві вже більше року, і він справді добре.
Девід Роббінс

Btw - щоб увімкнути підтримку скриптів C #, ви можете або інтегрувати бібліотеку сценаріїв C # всередину, або зробити компіляцію скриптів C # самостійно. Я зробив подібний легкий компілятор сценаріїв C # у своєму власному проекті тут: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs Компіляція сценарію C # трохи повільна (ніж, наприклад, у порівнянні з .Lua). сенс уникати зайвого кроку компіляції, якщо не потрібен.
ТармоПікаро

114

Особисто я б використовував C # як мову сценарію. Рамка .NET (і Mono, дякую Метью Шарлі) насправді включає компілятори для кожної мови .NET в самому рамках.

В основному, в реалізації цієї системи є 2 частини.

  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
FYI, і для всіх, хто цікавий, так, це компілюється і працює на Mono просто чудово. Потрібна лише посилання на систему для частин компіляції.
Меттью Шарлі

3
Це забруднює AppDomain
Daniel Little

4
@Lavinski Якщо ви не хочете, щоб він забруднив ваш AppDomain, просто просто створіть нову (що, мабуть, хороша ідея, так що ви можете розмістити більш жорстку безпеку на "сценаріях")
Грант Петерс,

3
@Lander - Це надзвичайно легка вага. Вищевказаний код - це ціла програма, яка підтримує "сценарій". csscript.netвиглядає, що це якась обгортка над цим. Це в основному реалізація голих кісток. Я думаю, що кращим питанням було б "що робить csscript.net для мене, що це не так". Я не знаю, що робить csscript, але вони, безумовно, знають, що робить вищезгаданий код, у них є (або щось надзвичайно схоже) у своїй бібліотеці.
Грант Петерс


19

Двигун PowerShell був розроблений так, щоб він легко вбудовувався в додаток, щоб зробити його можливим для написання сценарію. Насправді, PowerShell CLI - це просто текстовий інтерфейс для двигуна.

Редагувати: Див. Https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/


Я думаю, що це найкраще рішення, оскільки воно не додає об’єктів до AppDomain. У .Net ви ніколи не можете розвантажувати збірки чи класи.
Даніель Літтл

12

Мова бу .


1
Виглядає мертвим, якщо це проект за адресою github.com/boo-lang/boo
kristianp

@kristianp станом на даний момент, в проекті є щомісяця кілька комісій (до недавнього місяця тому). Так, можливо, він втратив трохи тяги, але все одно, здається, живий.
Tamás Szelei

8

Моєю мовою вибору сценарію була б Луа в наші дні. Це невеликий, швидкий, чистий, повністю документований, добре підтримуваний, має велике співтовариство , його використовують багато великих компаній галузі (Adobe, Blizzard, EA Games), напевно варто спробувати.

Щоб використовувати його з мовами .NET, проект LuaInterface надасть все необхідне.


1
Lua також використовується для сценаріїв у моді Гаррі, що це чудова гра :)
анонімний боягуз



2

Чергове голосування за IronPython. Вбудовувати його просто, взаємодія з класами .Net - це просто, і, ну, це Python.


1

Я можу запропонувати S #, яку я зараз підтримую. Це проект з відкритим кодом, написаний на C # і розроблений для додатків .NET.

Спочатку (2007-2009) він розміщувався за адресою http://www.codeplex.com/scriptdotnet , але нещодавно його перенесли в github.


Дякуємо, що опублікували свою відповідь! Будь ласка, уважно прочитайте FAQ щодо самореклами . Також зауважте, що ви зобов’язані публікувати відмову щоразу, коли ви посилаєтесь на свій власний сайт / продукт.
Ендрю Барбер

Дякую за пораду Ендрю. Одна з попередніх відповідей містить застаріле посилання на цю мову сценаріїв. Я чомусь не можу додати коментар до оригінальної відповіді, тому я опублікував новий, щоб надати правильне посилання.
Пітер



0

Я щойно створив плагін для клієнта, що дозволяє йому записувати код C # в модулі, які діють так, як це робить VBA для Office.



0

Мені подобається сценарій із самим C # . Зараз, у 2013 році, існує досить гарна підтримка сценаріїв C #, все більше бібліотек для нього стають доступними.

Mono має чудову підтримку коду сценарію C # , і ви можете використовувати його з .NET, просто включивши його Mono.CSharp.dllу свою програму. Для програми C # сценаріїв, яку я зробив, перевірте CShell

Також перевірте `ScriptEngine» в Рослин , яка від Microsoft, але це тільки CTP.

Як вже згадували деякі люди, CS-Script також існує досить давно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.