¿Cuál es el mejor lenguaje de secuencias de comandos para incrustar en una aplicación de escritorio C #? [cerrado]

98

Estamos escribiendo una aplicación de escritorio rica y compleja y necesitamos ofrecer flexibilidad en los formatos de informes, por lo que pensamos en exponer nuestro modelo de objetos a un lenguaje de secuencias de comandos. Hubo un tiempo en que eso significaba VBA (que sigue siendo una opción), pero el derivado del código administrado VSTA (creo) parece haberse marchitado en la vid.

¿Cuál es ahora la mejor opción para un lenguaje de secuencias de comandos integrado en Windows .NET?

Ewan Makepeace
fuente
FWIW, comencé con el enfoque en la respuesta de @ GrantPeter, usé un AppDomain para permitir la descarga, manejé la renovación de arrendamiento de objetos entre dominios y jugué con las medidas de seguridad de la zona de pruebas. El script compilado puede llamar a métodos en el programa principal, de regreso a través del límite de AppDomain. El experimento se puede encontrar aquí: github.com/fadden/DynamicScriptSandbox
fadden

Respuestas:

24

He usado CSScript con resultados asombrosos. Realmente redujo el tener que hacer enlaces y otras cosas de bajo nivel en mis aplicaciones con secuencias de comandos.

Héctor Sosa Jr
fuente
1
He estado usando CS-Script en producción durante más de un año y ha funcionado muy bien.
David Robbins
Por cierto, para habilitar la compatibilidad con scripts de C #, puede integrar la biblioteca de scripts de C # en su interior o realizar la compilación de scripts de C # por su cuenta. He hecho un compilador de script de C # ligero similar en mi propio proyecto aquí: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs La compilación de script de C # en sí es un poco lenta (que, por ejemplo, en comparación con .Lua), hace sentido para evitar un paso de compilación adicional si no es necesario.
TarmoPikaro
114

Personalmente, usaría C # como lenguaje de secuencias de comandos. El marco .NET (y Mono, gracias Matthew Scharley) en realidad incluye los compiladores para cada uno de los lenguajes .NET en el marco mismo.

Básicamente, hay 2 partes para la implementación de este sistema.

  1. Permitir que el usuario compile el código.Esto es relativamente fácil y se puede hacer en solo unas pocas líneas de código (aunque es posible que desee agregar un cuadro de diálogo de error, que probablemente sería un par de docenas de líneas de código más, dependiendo de cuán utilizable quieres que sea).

  2. Crear y usar clases contenidas en el ensamblado compilado. Esto es un poco más difícil que el paso anterior (requiere un poco de reflexión). Básicamente, debería tratar el ensamblado compilado como un "complemento" para el programa. Hay bastantes tutoriales sobre varias formas en que puede crear un sistema de complementos en C # (Google es su amigo).

Implementé una aplicación "rápida" para demostrar cómo se puede implementar este sistema (¡incluye 2 scripts de trabajo!). Este es el código completo para la aplicación, simplemente cree uno nuevo y pegue el código en el archivo "program.cs". En este punto, debo disculparme por la gran parte del código que estoy a punto de pegar (no tenía la intención de que fuera tan grande, pero me dejé llevar un poco con mis comentarios).


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
fuente
2
¿Sabe si esto también funcionará en Mono, o solo está disponible en .NET?
Matthew Scharley
4
Para su información, y para cualquier otra persona que tenga curiosidad, sí, esto se compila y se ejecuta en Mono muy bien. Solo necesita la referencia del sistema para las partes de compilación.
Matthew Scharley
3
Hacer esto contamina el AppDomain
Daniel Little
4
@Lavinski Si no quiere que contamine su AppDomain, simplemente cree uno nuevo (lo cual probablemente sea una buena idea de todos modos para que pueda colocar una seguridad más estricta en los "scripts")
Grant Peters
3
@Lander: esto es extremadamente ligero. El código anterior es un programa completo que admite "secuencias de comandos". csscript.netparece que es una especie de envoltorio sobre esto. Esta es básicamente una implementación básica. Creo que una mejor pregunta sería "¿qué hace csscript.net por mí que esto no haga?". No sé qué está haciendo csscript, pero definitivamente saben lo que hace el código anterior aquí, lo tienen (o algo extremadamente similar) en su biblioteca.
Grant Peters
19

El motor PowerShell se diseñó para integrarse fácilmente en una aplicación para que se pueda programar. De hecho, la CLI de PowerShell es solo una interfaz basada en texto para el motor.

Editar: consulte https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/

Rodrick Chapman
fuente
Creo que esta es la mejor solución porque no agrega objetos al AppDomain. En .Net nunca se pueden descargar ensamblados ni clases.
Daniel Little
12

Boo lenguaje.

jop
fuente
1
Parece muerto si es el proyecto en github.com/boo-lang/boo
kristianp
@kristianp a partir de ahora, hay algunas confirmaciones cada mes en el proyecto (hasta hace tan solo un mes). Entonces, tal vez haya perdido un poco de tracción, pero aún parece estar vivo.
Tamás Szelei
8

Mi lenguaje de escritura de elección sería Lua en estos días. Es pequeño, rápido, limpio, completamente documentado, bien respaldado, tiene una gran comunidad , es utilizado por muchas grandes empresas de la industria (Adobe, Blizzard, EA Games), definitivamente vale la pena intentarlo.

Para usarlo con lenguajes .NET, el proyecto LuaInterface le proporcionará todo lo que necesita.

Remo.D
fuente
1
Lua también se usa para escribir guiones en Garry's Mod, que es un gran juego :)
cobarde anónimo
2

IronRuby como se mencionó anteriormente. Un proyecto interesante para mí como programador de C # es el soporte de C # Eval en Mono . Pero aún no está disponible (será parte de Mono 2.2).

Borek Bernard
fuente
2

Otro voto para IronPython. Incrustarlo es simple, la interoperación con las clases .Net es sencilla y, bueno, es Python.

Robert Rossney
fuente
1

Puedo sugerir S # que actualmente mantengo. Es un proyecto de código abierto, escrito en C # y diseñado para aplicaciones .NET.

Inicialmente (2007-2009) estaba alojado en http://www.codeplex.com/scriptdotnet , pero recientemente se movió a github.

Pedro
fuente
¡Gracias por publicar tu respuesta! Asegúrese de leer detenidamente las preguntas frecuentes sobre la autopromoción. También tenga en cuenta que es necesario que publique un descargo de responsabilidad cada vez que enlace a su propio sitio / producto.
Andrew Barber
Gracias por el consejo Andrew. Una de las respuestas anteriores contiene un enlace desactualizado a este lenguaje de secuencias de comandos. Por alguna razón, no puedo agregar comentarios a la respuesta original, por lo que publiqué uno nuevo para proporcionar el enlace correcto.
Peter
1

Prueba Ela . Este es un lenguaje funcional similar a Haskell y puede integrarse en cualquier aplicación .Net. Incluso tiene un IDE simple pero utilizable.

Алексей Богатырев
fuente
0

Acabo de crear un complemento para un cliente, lo que les permite escribir código C # en módulos que actúan como lo hace VBA para Office.


fuente
0

Me gusta programar con C # en sí . Ahora, en 2013, hay un soporte bastante bueno para las secuencias de comandos C #, y cada vez hay más bibliotecas disponibles.

Mono tiene un gran soporte para el código de script C # , y puede usarlo con .NET simplemente incluyendo el Mono.CSharp.dllen su aplicación. Para la aplicación de secuencias de comandos C # que hice, consulte CShell

Consulte también el 'ScriptEngine' en Roslyn, que es de Microsoft, pero esto es solo CTP.

Como algunas personas ya han mencionado, CS-Script también ha existido durante bastante tiempo.

lukebuehler
fuente