Jaki jest najlepszy język skryptowy do osadzenia w aplikacji klasycznej C #? [Zamknięte]

98

Piszemy złożoną, bogatą aplikację komputerową i musimy oferować elastyczność formatów raportowania, więc pomyśleliśmy, że po prostu przedstawimy nasz model obiektowy językowi skryptowemu. Był czas, kiedy oznaczało to VBA (który nadal jest opcją), ale zarządzany pochodna kodu VSTA (myślę) wydaje się, że uschło na winorośli.

Jaki jest obecnie najlepszy wybór dla wbudowanego języka skryptowego w systemie Windows .NET?

Ewan Makepeace
źródło
FWIW, zacząłem od podejścia podanego w odpowiedzi @ GrantPeter, użyłem AppDomain, aby umożliwić wyładowanie, zająłem się odnowieniem dzierżawy obiektu między domenami i bawiłem się środkami bezpieczeństwa w piaskownicy. Skompilowany skrypt może wywoływać metody w głównym programie, z powrotem przez granicę AppDomain. Eksperyment można znaleźć tutaj: github.com/fadden/DynamicScriptSandbox
fadden

Odpowiedzi:

24

Użyłem CSScriptu z niesamowitymi wynikami. To naprawdę ograniczyło konieczność tworzenia powiązań i innych rzeczy niskiego poziomu w moich skryptowych aplikacjach.

Hector Sosa Jr
źródło
1
Używam CS-Script w produkcji od ponad roku i działa naprawdę dobrze.
David Robbins
Btw - aby włączyć obsługę skryptów C #, możesz zintegrować bibliotekę skryptów C # wewnątrz lub samodzielnie skompilować skrypt C #. Zrobiłem podobny lekki kompilator skryptów C # w moim własnym projekcie tutaj: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs Sama kompilacja skryptów C # jest nieco powolna (niż na przykład w porównaniu do .Lua), sprawia, że sens unikania dodatkowego kroku kompilacji, jeśli nie jest potrzebny.
TarmoPikaro
114

Osobiście użyłbym C # jako języka skryptowego. Framework .NET (i Mono, dzięki Matthew Scharley) w rzeczywistości zawiera kompilatory dla każdego języka .NET w samym frameworku.

Zasadniczo implementacja tego systemu składa się z 2 części.

  1. Pozwól użytkownikowi skompilować kod Jest to stosunkowo łatwe i można to zrobić tylko w kilku wierszach kodu (chociaż możesz chcieć dodać okno dialogowe błędu, które prawdopodobnie będzie kilkadziesiąt kolejnych wierszy kodu, w zależności od tego jak użyteczne chcesz, żeby tak było).

  2. Tworzenie i używanie klas zawartych w skompilowanym zestawie Jest to trochę trudniejsze niż poprzedni krok (wymaga odrobiny refleksji). Zasadniczo powinieneś po prostu traktować skompilowany zestaw jako „wtyczkę” programu. Istnieje wiele samouczków na temat różnych sposobów tworzenia systemu wtyczek w C # (Google jest twoim przyjacielem).

Zaimplementowałem „szybką” aplikację, aby zademonstrować, jak można wdrożyć ten system (zawiera 2 działające skrypty!). To jest kompletny kod aplikacji, wystarczy utworzyć nowy i wkleić kod do pliku „program.cs”. W tym momencie muszę przeprosić za duży fragment kodu, który zamierzam wkleić (nie zamierzałem, aby był tak duży, ale trochę mnie poniosło)


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
                        }
                    }
                }
            }
        }
    }
}

Grant Peters
źródło
2
Czy wiesz, czy to zadziała również w Mono, czy też jest dostępne tylko w .NET?
Matthew Scharley
4
FYI, a dla każdego, kto jest ciekawy, tak, kompiluje się i działa na Mono dobrze. Potrzebujesz tylko odniesienia do systemu dla części kompilacji.
Matthew Scharley
3
Takie postępowanie zanieczyszcza AppDomain
Daniel Little
4
@Lavinski Jeśli nie chcesz, aby zanieczyszczało Twoją domenę AppDomain, po prostu stwórz nową (co i tak jest prawdopodobnie dobrym pomysłem, abyś mógł umieścić bardziej rygorystyczne zabezpieczenia w „skryptach”)
Grant Peters
3
@Lander - To jest niezwykle lekkie. Powyższy kod to cały program obsługujący „skrypty”. csscript.netwygląda na to, że jest to jakiś rodzaj opakowania. Jest to w zasadzie implementacja podstawowa. Myślę, że lepszym pytaniem byłoby „co robi dla mnie csscript.net, a co nie”. Nie wiem, co robi csscript, ale na pewno wiedzą, co robi powyższy kod, mają go (lub coś bardzo podobnego) w swojej bibliotece.
Grant Peters
19

Silnik PowerShell został zaprojektowany w taki sposób, aby można go było łatwo osadzić w aplikacji, aby była ona skryptowalna. W rzeczywistości interfejs wiersza polecenia programu PowerShell to tylko tekstowy interfejs do silnika.

Edycja: zobacz https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/

Rodrick Chapman
źródło
Myślę, że to najlepsze rozwiązanie, ponieważ nie dodaje obiektów do AppDomain. W .Net nigdy nie można zwolnić zestawów ani klas.
Daniel Little
12

Język boo .

jop
źródło
1
Wygląda na martwego, jeśli to projekt na github.com/boo-lang/boo
kristianp
@kristianp od teraz, co miesiąc w projekcie jest kilka zatwierdzeń (jeszcze miesiąc temu). Może więc trochę stracił przyczepność, ale nadal wydaje się, że żyje.
Tamás Szelei
8

W dzisiejszych czasach moim ulubionym językiem skryptowym byłby Lua . Jest mały, szybki, czysty, w pełni udokumentowany, dobrze obsługiwany, ma świetną społeczność , jest używany przez wiele dużych firm z branży (Adobe, Blizzard, EA Games), zdecydowanie warto spróbować.

Aby używać go z językami .NET, projekt LuaInterface zapewni wszystko, czego potrzebujesz.

Remo.D
źródło
1
Lua jest również używana do tworzenia skryptów w Garry's Mod, co jest świetną grą :)
anonimowy tchórz.
2

IronRuby, jak wspomniano powyżej. Ciekawym projektem dla mnie jako programisty C # jest obsługa C # Eval w Mono . Ale nie jest jeszcze dostępny (będzie częścią Mono 2.2).

Borek Bernard
źródło
2

Kolejny głos na IronPython. Osadzanie go jest proste, współdziałanie z klasami .Net jest proste i, cóż, to jest Python.

Robert Rossney
źródło
1

Mogę zasugerować S #, które obecnie utrzymuję. Jest to projekt open source, napisany w C # i przeznaczony dla aplikacji .NET.

Początkowo (2007-2009) był hostowany pod adresem http://www.codeplex.com/scriptdotnet , ale ostatnio został przeniesiony na github.

Piotr
źródło
Dziękujemy za przesłanie odpowiedzi! Przeczytaj uważnie często zadawane pytania dotyczące autopromocji . Należy również pamiętać, że jest to konieczne , aby umieścić disclaimer każdym razem, gdy odwołują się do swojej własnej strony / produktu.
Andrew Barber
Dzięki za radę Andrew. Jedna z poprzednich odpowiedzi zawiera nieaktualne łącze do tego języka skryptowego. Z jakiegoś powodu nie mogę dodać komentarza do oryginalnej odpowiedzi, dlatego opublikowałem nowy, aby podać poprawny link.
Peter
1

Wypróbuj Eli . Jest to język funkcjonalny podobny do języka Haskell i może być osadzony w dowolnej aplikacji .Net. Nawet ma proste, ale użyteczne IDE.

Алексей Богатырев
źródło
0

Właśnie stworzyłem wtyczkę dla klienta, umożliwiającą pisanie kodu C # w modułach, które działają jak VBA dla Office.


źródło
0

Lubię pisać skrypty z samym C # . Teraz, w 2013 roku, jest całkiem dobre wsparcie dla skryptów C #, dostępnych jest coraz więcej bibliotek.

Mono ma świetne wsparcie dla skryptowania kodu C # i możesz go używać z .NET, po prostu dołączając do Mono.CSharp.dllaplikacji. W przypadku aplikacji skryptowej C #, którą zrobiłem, sprawdź CShell

Sprawdź także `` ScriptEngine '' w Roslyn, który pochodzi od firmy Microsoft, ale to tylko CTP.

Jak niektórzy już wspominali, CS-Script istnieje już od dłuższego czasu.

lukebuehler
źródło