Qual è il miglior linguaggio di scripting da incorporare in un'applicazione desktop C #? [chiuso]


98

Stiamo scrivendo un'applicazione desktop ricca e complessa e dobbiamo offrire flessibilità nei formati di report, quindi abbiamo pensato di esporre il nostro modello a oggetti a un linguaggio di scripting. Il tempo era quando ciò significava VBA (che è ancora un'opzione), ma il derivato del codice gestito VSTA (credo) sembra essersi appassito sulla vite.

Qual è ora la scelta migliore per un linguaggio di scripting incorporato su Windows .NET?


FWIW, ho iniziato con l'approccio nella risposta di @ GrantPeter, ho utilizzato un AppDomain per consentire lo scaricamento, gestito il rinnovo del lease di oggetti tra domini e armeggiato con le misure di sicurezza sandbox. Lo script compilato può chiamare metodi nel programma principale, di nuovo oltre il confine AppDomain. L'esperimento può essere trovato qui: github.com/fadden/DynamicScriptSandbox
fadden

Risposte:


24

Ho usato CSScript con risultati sorprendenti. Ha davvero ridotto la necessità di eseguire associazioni e altre cose di basso livello nelle mie app con script.


1
Uso CS-Script in produzione da oltre un anno e ha funzionato molto bene.
David Robbins

A proposito: per abilitare il supporto per script C # è possibile integrare la libreria di script C # all'interno o creare autonomamente la compilazione di script C #. Ho fatto un simile compilatore di script C # leggero nel mio progetto qui: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs La compilazione di script C # stessa è un po 'lenta (rispetto ad esempio al confronto con .Lua), rende ha senso evitare passaggi di compilazione aggiuntivi se non necessari.
TarmoPikaro

114

Personalmente, userei C # come linguaggio di scripting. Il framework .NET (e Mono, grazie Matthew Scharley) include effettivamente i compilatori per ciascuno dei linguaggi .NET nel framework stesso.

Fondamentalmente, ci sono 2 parti nell'implementazione di questo sistema.

  1. Consentire all'utente di compilare il codice È relativamente facile e puòessere fatto solo con poche righe di codice (anche se potresti voler aggiungere una finestra di dialogo di errore, che probabilmente sarebbe un paio di dozzine di righe di codice in più, a seconda di come è utilizzabile vuoi che sia).

  2. Creare e usare classi contenute nell'assembly compilato Questo è un po 'più difficile del passaggio precedente (richiede un po' di riflessione). Fondamentalmente, dovresti trattare l'assembly compilato come un "plug-in" per il programma. Ci sono alcuni tutorial su vari modi in cui puoi creare un sistema di plug-in in C # (Google è tuo amico).

Ho implementato un'applicazione "rapida" per dimostrare come implementare questo sistema (include 2 script funzionanti!). Questo è il codice completo dell'applicazione, basta crearne uno nuovo e incollare il codice nel file "program.cs". A questo punto devo scusarmi per il grosso pezzo di codice che sto per incollare (non intendevo che fosse così grande, ma mi sono lasciato un po 'trasportare dai miei commenti)


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
Sai se funzionerà anche in Mono o è disponibile solo su .NET?
Matthew Scharley

4
Cordiali saluti, e per chiunque altro sia curioso, sì, questo si compila e funziona su Mono perfettamente. Serve solo il riferimento di sistema per le parti della compilation.
Matthew Scharley

3
In questo modo si inquina l'AppDomain
Daniel Little

4
@Lavinski Se non vuoi che inquini il tuo AppDomain, creane uno nuovo (che probabilmente è comunque una buona idea in modo da poter mettere una sicurezza più rigorosa sugli "script")
Grant Peters

3
@Lander - Questo è estremamente leggero. Il codice sopra è un intero programma che supporta lo "scripting". csscript.netsembra che sia una sorta di involucro su questo. Questa è fondamentalmente un'implementazione essenziale. Penso che una domanda migliore sarebbe "cosa fa csscript.net per me che questo non fa". Non so cosa stia facendo csscript, ma sanno sicuramente cosa fa il codice sopra qui, lo hanno (o qualcosa di estremamente simile) nella loro libreria.
Grant Peters


19

Il motore di PowerShell è stato progettato per essere facilmente incorporato in un'applicazione per renderlo utilizzabile tramite script. In effetti, la CLI di PowerShell è solo un'interfaccia basata su testo per il motore.

Modifica: vedi https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/


Penso che questa sia la soluzione migliore perché non aggiunge oggetti ad AppDomain. In .Net non puoi mai scaricare assembly o classi.
Daniel Little


8

Il mio linguaggio di scripting preferito sarebbe Lua in questi giorni. È piccolo, veloce, pulito, completamente documentato, ben supportato, ha una grande comunità , è utilizzato da molte grandi aziende del settore (Adobe, Blizzard, EA Games), sicuramente vale la pena provare.

Per usarlo con i linguaggi .NET il progetto LuaInterface fornirà tutto ciò di cui hai bisogno.


1
Lua è anche usato per la sceneggiatura in Garry's Mod, che è un ottimo gioco :)
codardo anonimo



2

Un altro voto per IronPython. Incorporarlo è semplice, l'interoperabilità con le classi .Net è semplice e, beh, è ​​Python.


1

Potrei suggerire S # che attualmente mantengo. È un progetto open source, scritto in C # e progettato per applicazioni .NET.

Inizialmente (2007-2009) era ospitato su http://www.codeplex.com/scriptdotnet , ma recentemente è stato spostato su github.


Grazie per aver pubblicato la tua risposta! Assicurati di leggere attentamente le FAQ sull'autopromozione . Si noti inoltre che è necessario pubblicare un disclaimer ogni volta che si collega al proprio sito / prodotto.
Andrew Barber

Grazie per il consiglio Andrew. Una delle risposte precedenti contiene un collegamento obsoleto a questo linguaggio di scripting. Per qualche motivo non posso aggiungere commenti alla risposta originale, quindi ne ho pubblicato uno nuovo per fornire il collegamento corretto.
Peter



0

Ho appena creato un plug-in per un client, consentendo loro di scrivere codice C # in moduli che agiscono come VBA fa per Office.



0

Mi piace lo scripting con lo stesso C # . Ora, nel 2013, c'è un supporto abbastanza buono per lo scripting C #, per questo stanno diventando disponibili sempre più librerie.

Mono ha un ottimo supporto per lo script di codice C # e puoi usarlo con .NET semplicemente includendo Mono.CSharp.dllnella tua applicazione. Per l'applicazione di scripting C # che ho creato, controlla CShell

Controlla anche lo "ScriptEngine" in Roslyn che è di Microsoft, ma questo è solo CTP.

Come alcune persone hanno già detto, anche CS-Script è in circolazione da un po 'di tempo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.