Was ist die beste Skriptsprache zum Einbetten in eine C # -Desktopanwendung? [geschlossen]


98

Wir schreiben eine komplexe, umfangreiche Desktop-Anwendung und müssen Flexibilität bei den Berichtsformaten bieten, sodass wir dachten, wir würden unser Objektmodell nur einer Skriptsprache aussetzen. Zu dieser Zeit bedeutete dies VBA (was immer noch eine Option ist), aber das verwaltete Code-Derivat VSTA (glaube ich) scheint am Rebstock verdorrt zu sein.

Was ist jetzt die beste Wahl für eine eingebettete Skriptsprache unter Windows .NET?


FWIW, ich habe mit dem Ansatz in der Antwort von @ GrantPeter begonnen, eine AppDomain verwendet, um das Entladen zu ermöglichen, die Erneuerung der domänenübergreifenden Objektmiete durchgeführt und mit Sandbox-Sicherheitsmaßnahmen herumgespielt. Das kompilierte Skript kann Methoden im Hauptprogramm über die AppDomain-Grenze hinweg aufrufen. Das Experiment kann hier gefunden werden: github.com/fadden/DynamicScriptSandbox
fadden

Antworten:


24

Ich habe CSScript mit erstaunlichen Ergebnissen verwendet. Es hat wirklich dazu beigetragen, dass ich in meinen skriptfähigen Apps Bindungen und andere Dinge auf niedriger Ebene ausführen muss.


1
Ich benutze CS-Script seit über einem Jahr in der Produktion und es hat sich sehr gut bewährt.
David Robbins

Übrigens: Um die Unterstützung von C # -Skripten zu aktivieren, können Sie entweder die C # -Skriptbibliothek integrieren oder die C # -Skriptkompilierung selbst durchführen. Ich habe hier in meinem eigenen Projekt einen ähnlich leichten C # -Skript-Compiler erstellt: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs Die C # -Skript-Kompilierung selbst ist etwas langsam (als zum Beispiel im Vergleich zu .Lua) Es ist sinnvoll, zusätzliche Kompilierungsschritte zu vermeiden, wenn diese nicht benötigt werden.
TarmoPikaro

114

Persönlich würde ich C # als Skriptsprache verwenden. Das .NET-Framework (und Mono, danke Matthew Scharley) enthält tatsächlich die Compiler für jede der .NET-Sprachen im Framework selbst.

Grundsätzlich besteht die Implementierung dieses Systems aus zwei Teilen.

  1. Ermöglichen Sie dem Benutzer das Kompilieren des Codes Dies ist relativ einfach und kann in nur wenigen Codezeilen durchgeführt werden (obwohl Sie möglicherweise einen Fehlerdialog hinzufügen möchten, der je nach Verwendbarkeit wahrscheinlich ein paar Dutzend weitere Codezeilen umfassen würde du willst es sein).

  2. Erstellen und Verwenden von Klassen, die in der kompilierten Assembly enthalten sind Dies ist etwas schwieriger als der vorherige Schritt (erfordert ein wenig Nachdenken). Grundsätzlich sollten Sie die kompilierte Assembly nur als "Plug-In" für das Programm behandeln. Es gibt einige Tutorials zu verschiedenen Möglichkeiten, wie Sie ein Plug-In-System in C # erstellen können (Google ist Ihr Freund).

Ich habe eine "schnelle" Anwendung implementiert, um zu demonstrieren, wie Sie dieses System implementieren können (enthält 2 Arbeitsskripte!). Dies ist der vollständige Code für die Anwendung. Erstellen Sie einfach einen neuen und fügen Sie den Code in die Datei "program.cs" ein. An dieser Stelle muss ich mich für den großen Teil des Codes entschuldigen, den ich einfügen möchte (ich hatte nicht vor, dass er so groß ist, wurde aber von meinen Kommentaren ein wenig mitgerissen).


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
Wissen Sie, ob dies auch in Mono funktioniert oder nur in .NET verfügbar ist?
Matthew Scharley

4
Zu Ihrer Information, und für alle anderen, die neugierig sind, ja, dies kompiliert und läuft auf Mono ganz gut. Benötigen Sie nur die Systemreferenz für die Kompilierungsteile.
Matthew Scharley

3
Dies verschmutzt die AppDomain
Daniel Little

4
@Lavinski Wenn Sie nicht möchten, dass Ihre AppDomain dadurch verschmutzt wird, erstellen Sie einfach eine neue (was wahrscheinlich sowieso eine gute Idee ist, damit Sie die "Skripte" strenger schützen können)
Grant Peters

3
@ Lander - Dies ist extrem leicht. Der obige Code ist ein ganzes Programm, das "Scripting" unterstützt. csscript.netsieht aus wie es eine Art Wrapper darüber ist. Dies ist im Grunde eine reine Implementierung. Ich denke, eine bessere Frage wäre "Was macht csscript.net für mich, was dies nicht tut". Ich weiß nicht, was csscript tut, aber sie wissen definitiv, was der obige Code hier tut, sie haben ihn (oder etwas sehr ähnliches) in ihrer Bibliothek.
Grant Peters


19

Die PowerShell-Engine wurde so konzipiert, dass sie problemlos in eine Anwendung eingebettet werden kann, um sie skriptfähig zu machen. Tatsächlich ist die PowerShell-CLI nur eine textbasierte Schnittstelle zur Engine.

Bearbeiten: Siehe https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/


Ich denke, dies ist die beste Lösung, da der AppDomain keine Objekte hinzugefügt werden. In .Net können Sie niemals Assemblys oder Klassen entladen.
Daniel Little


8

Meine bevorzugte Skriptsprache wäre heutzutage Lua . Es ist klein, schnell, sauber, vollständig dokumentiert, gut unterstützt, hat eine großartige Community und wird von vielen großen Unternehmen der Branche (Adobe, Blizzard, EA Games) verwendet. Es ist definitiv einen Versuch wert.

Um es mit .NET-Sprachen zu verwenden, bietet das LuaInterface- Projekt alles, was Sie benötigen.


1
Lua wird auch für Skripte in Garry's Mod verwendet, was ein großartiges Spiel ist :)
anonymer Feigling



2

Eine weitere Abstimmung für IronPython. Das Einbetten ist einfach, die Interaktion mit .NET-Klassen ist unkompliziert, und es ist Python.


1

Ich kann S # vorschlagen, das ich derzeit pflege. Es ist ein Open Source-Projekt, das in C # geschrieben und für .NET-Anwendungen entwickelt wurde.

Ursprünglich (2007-2009) wurde es unter http://www.codeplex.com/scriptdotnet gehostet , aber kürzlich wurde es auf Github verschoben.


Vielen Dank für Ihre Antwort! Bitte lesen Sie die FAQ zur Eigenwerbung sorgfältig durch. Beachten Sie auch , dass Sie jedes Mal, wenn Sie auf Ihre eigene Website / Ihr eigenes Produkt verlinken, einen Haftungsausschluss veröffentlichen müssen.
Andrew Barber

Danke für den Rat Andrew. Eine der vorherigen Antworten enthält einen veralteten Link zu dieser Skriptsprache. Aus irgendeinem Grund kann ich der ursprünglichen Antwort keinen Kommentar hinzufügen, daher habe ich einen neuen veröffentlicht, um den richtigen Link bereitzustellen.
Peter



0

Ich habe gerade ein Plugin für einen Client erstellt, mit dem er C # -Code in Modulen schreiben kann, die sich wie VBA für Office verhalten.



0

Ich mag Skripte mit C # selbst . Jetzt, im Jahr 2013, gibt es eine recht gute Unterstützung für C # -Skripte, es werden immer mehr Bibliotheken dafür verfügbar.

Mono bietet eine hervorragende Unterstützung für das Schreiben von C # -Code . Sie können es mit .NET verwenden, indem Sie es einfach Mono.CSharp.dllin Ihre Anwendung aufnehmen. Für C # -Skriptanwendungen , die ich erstellt habe, lesen Sie CShell

Schauen Sie sich auch die "ScriptEngine" in Roslyn an, die von Microsoft stammt, aber dies ist nur CTP.

Wie einige bereits erwähnt haben, gibt es CS-Script auch schon eine ganze Weile.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.