Какой язык сценариев лучше всего встраивать в настольное приложение C #? [закрыто]

98

Мы пишем сложное полнофункциональное настольное приложение, и нам необходимо обеспечить гибкость форматов отчетов, поэтому мы подумали, что просто представим нашу объектную модель на языке сценариев. Было время, когда это означало VBA (который все еще возможен), но производная VSTA с управляемым кодом (я думаю), похоже, исчезла на корню.

Что сейчас является лучшим выбором для встроенного языка сценариев в Windows .NET?

Эван Мейкпис
источник
FWIW, я начал с подхода в ответе @ GrantPeter, использовал AppDomain, чтобы разрешить выгрузку, обработал обновление аренды междоменного объекта и возился с мерами безопасности песочницы. Скомпилированный скрипт может вызывать методы в основной программе обратно через границу домена приложений. Эксперимент можно найти здесь: github.com/fadden/DynamicScriptSandbox
fadden

Ответы:

24

Я использовал CSScript с потрясающими результатами. Это действительно сократило необходимость делать привязки и другие вещи низкого уровня в моих скриптовых приложениях.

Гектор Соса-младший
источник
1
Я использую CS-Script в продакшене более года, и он отлично зарекомендовал себя.
Дэвид Роббинс
Кстати, чтобы включить поддержку сценариев C #, вы можете либо интегрировать библиотеку сценариев C # внутри, либо самостоятельно выполнить компиляцию сценариев C #. Я сделал аналогичный легкий компилятор сценария C # в своем собственном проекте здесь: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs Сама компиляция сценария C # немного медленнее (чем, например, по сравнению с .Lua), делает имеет смысл избежать лишнего шага компиляции, если он не нужен.
TarmoPikaro
114

Лично я бы использовал C # в качестве языка сценариев. Платформа .NET (и Mono, спасибо Мэтью Шарли) фактически включает в себя компиляторы для каждого из языков .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?
Мэтью Шарли, 01
4
К вашему сведению, и для всех, кому интересно, да, это действительно хорошо компилируется и работает на Mono. Справочник по системе нужен только для частей компиляции.
Мэтью Шарли, 02
3
Это загрязняет AppDomain
Дэниел Литтл
4
@Lavinski Если вы не хотите, чтобы он загрязнял ваш домен приложений, просто создайте новый (что, вероятно, в любом случае является хорошей идеей, чтобы вы могли установить более строгие меры безопасности для «скриптов»)
Грант Питерс
3
@Lander - это очень легкий. Приведенный выше код представляет собой целую программу, поддерживающую «скриптинг». csscript.netпохоже, это какая-то обертка над этим. По сути, это простая реализация. Я думаю, что лучше было бы спросить: «Что csscript.net делает для меня, а это нет». Я не знаю, что делает csscript, но они точно знают, что делает приведенный выше код, у них он (или что-то очень похожее) есть в их библиотеке.
Грант Питерс
19

Механизм PowerShell был разработан так, чтобы его можно было легко встроить в приложение, чтобы сделать его доступным для сценариев. Фактически, PowerShell CLI - это просто текстовый интерфейс для движка.

Изменить: см. Https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/

Родрик Чепмен
источник
Я думаю, что это лучшее решение, потому что оно не добавляет объекты в AppDomain. В .Net вы никогда не сможете выгружать сборки или классы.
Дэниел Литтл
12

Бу язык.

Jop
источник
1
Выглядит мертвым, если это проект на github.com/boo-lang/boo
kristianp
@kristianp на данный момент в проекте каждый месяц выполняется несколько коммитов (до последнего месяца назад). Так что, возможно, он немного потерял сцепление с дорогой, но, похоже, все еще жив.
Tamás Szelei
8

Сейчас я предпочитаю язык сценариев Lua . Он маленький, быстрый, чистый, полностью документированный, хорошо поддерживаемый, имеет отличное сообщество , он используется многими крупными компаниями в отрасли (Adobe, Blizzard, EA Games), безусловно, стоит попробовать.

Чтобы использовать его с языками .NET, проект LuaInterface предоставит все, что вам нужно.

Remo.D
источник
1
Lua также используется для написания сценариев в Garry's Mod, что является отличной игрой :)
анонимный трус
2

IronRuby, как упоминалось выше. Для меня как программиста на C # интересным проектом является поддержка C # Eval в Mono . Но он пока недоступен (будет частью Mono 2.2).

Борек Бернар
источник
2

Еще одно голосование за IronPython. Встраивать его просто, взаимодействие с классами .Net просто, ну и Python.

Роберт Россни
источник
1

Я могу предложить S #, который я поддерживаю в настоящее время. Это проект с открытым исходным кодом, написанный на C # и предназначенный для приложений .NET.

Первоначально (2007-2009) он размещался на http://www.codeplex.com/scriptdotnet , но недавно был перемещен на github.

Питер
источник
Спасибо, что разместили свой ответ! Не забудьте внимательно прочитать FAQ по саморекламе . Также обратите внимание, что необходимо публиковать отказ от ответственности каждый раз, когда вы ссылаетесь на свой собственный сайт / продукт.
Эндрю Барбер
Спасибо за совет Андрей. Один из предыдущих ответов содержит устаревшую ссылку на этот язык сценариев. По какой-то причине я не могу добавить комментарий к исходному ответу, поэтому я опубликовал новый, чтобы указать правильную ссылку.
Питер
1

Попробуйте Ela . Это функциональный язык, похожий на Haskell, и его можно встроить в любое приложение .Net. Даже у него есть простая, но удобная IDE.

Алексей Богатырев
источник
0

Я только что создал плагин для клиента, позволяющий им писать код C # в модулях, которые действуют так же, как VBA для Office.


источник
0

Раньше я использовал Lua ; в приложении Delphi, но его можно встроить во многие вещи. Он используется в Adobe Photoshop Lightroom .

Тони
источник
0

Мне нравится писать сценарии на самом C # . Сейчас, в 2013 году, появилась неплохая поддержка сценариев C #, для него становится доступно все больше и больше библиотек.

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

Также проверьте ScriptEngine в Roslyn от Microsoft, но это только CTP.

Как уже упоминали некоторые люди, CS-Script тоже существует довольно давно.

Lukebuehler
источник