Qual é a melhor linguagem de script para incorporar em um aplicativo de desktop C #? [fechadas]

98

Estamos escrevendo um aplicativo de desktop rico e complexo e precisamos oferecer flexibilidade nos formatos de relatórios, então pensamos em apenas expor nosso modelo de objeto a uma linguagem de script. Já era tempo que isso significava VBA (que ainda é uma opção), mas o derivado de código gerenciado VSTA (eu acho) parece ter murchado na videira.

Qual é agora a melhor escolha para uma linguagem de script incorporada no Windows .NET?

Ewan Makepeace
fonte
FWIW, comecei com a abordagem na resposta de @ GrantPeter, usei um AppDomain para permitir o descarregamento, lidei com a renovação da concessão do objeto de domínio cruzado e brinquei com as medidas de segurança da sandbox. O script compilado pode chamar métodos no programa principal, de volta ao limite do AppDomain. O experimento pode ser encontrado aqui: github.com/fadden/DynamicScriptSandbox
fadden

Respostas:

24

Usei o CSScript com resultados incríveis. Realmente reduziu a necessidade de fazer ligações e outras coisas de baixo nível em meus aplicativos com scripts.

Hector Sosa Jr
fonte
1
Tenho usado CS-Script em produção por mais de um ano e tem funcionado muito bem.
David Robbins de
A propósito - para habilitar o suporte a script C #, você pode integrar a biblioteca de scripts C # ou fazer a compilação de scripts C # por conta própria. Eu fiz um compilador de script C # leve semelhante em meu próprio projeto aqui: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs A própria compilação de script C # é um pouco lenta (do que, por exemplo, comparando com .Lua), torna sentido para evitar a etapa de compilação extra se não for necessário.
TarmoPikaro
114

Pessoalmente, eu usaria C # como linguagem de script. O framework .NET (e Mono, obrigado Matthew Scharley) na verdade inclui os compiladores para cada uma das linguagens .NET no próprio framework.

Basicamente, existem 2 partes para a implementação deste sistema.

  1. Permitir que o usuário compile o código Isso é relativamente fácil e pode ser feito em apenas algumas linhas de código (embora você possa querer adicionar uma caixa de diálogo de erro, que provavelmente seria mais algumas dezenas de linhas de código, dependendo de quão utilizável você quer que seja).

  2. Criar e usar classes contidas no assembly compilado Isso é um pouco mais difícil do que a etapa anterior (requer um pouco de reflexão). Basicamente, você deve apenas tratar o assembly compilado como um "plug-in" para o programa. Existem alguns tutoriais sobre várias maneiras de criar um sistema de plug-in em C # (o Google é seu amigo).

Eu implementei um aplicativo "rápido" para demonstrar como você pode implementar este sistema (inclui 2 scripts de trabalho!). Este é o código completo da aplicação, basta criar um novo e colar o código no arquivo "program.cs". Nesse ponto, devo me desculpar pelo grande pedaço de código que estou prestes a colar (não pretendia que fosse tão grande, mas me empolguei um pouco com meus comentários)


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
fonte
2
Você sabe se isso funcionará no Mono também ou está disponível apenas no .NET?
Matthew Scharley
4
Para sua informação, e para qualquer pessoa curiosa, sim, ele compila e roda no Mono muito bem. Só precisa da referência do sistema para as partes de compilação.
Matthew Scharley,
3
Fazer isso polui o AppDomain
Daniel Little,
4
@Lavinski Se você não quer poluir seu AppDomain, então simplesmente faça um novo (o que provavelmente é uma boa ideia de qualquer maneira para que você possa colocar uma segurança mais rigorosa nos "scripts")
Grant Peters
3
@Lander - É extremamente leve. O código acima é um programa completo que oferece suporte a "scripts". csscript.netparece que é uma espécie de invólucro sobre isso. Esta é basicamente uma implementação básica. Acho que uma pergunta melhor seria "o que csscript.net faz por mim que isso não faz". Não sei o que csscript está fazendo, mas eles definitivamente sabem o que o código acima faz, eles o têm (ou algo extremamente semelhante) em sua biblioteca.
Grant Peters
19

O mecanismo PowerShell foi projetado para ser facilmente incorporado em um aplicativo para torná-lo programável. Na verdade, o PowerShell CLI é apenas uma interface baseada em texto para o mecanismo.

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

Rodrick Chapman
fonte
Acho que essa é a melhor solução porque não adiciona objetos ao AppDomain. Em .net você nunca pode descarregar assemblies ou classes.
Daniel Little,
12

Língua de Boo .

pular
fonte
1
Parece morto se for o projeto em github.com/boo-lang/boo
kristianp
@kristianp a partir de agora, existem alguns commits todos os meses no projeto (até recentemente, um mês atrás). Talvez tenha perdido um pouco de tração, mas ainda parece estar vivo.
Tamás Szelei
8

Minha linguagem de script de escolha seria Lua hoje em dia. É pequeno, rápido, limpo, totalmente documentado, bem suportado, tem uma grande comunidade , é usado por muitas grandes empresas do setor (Adobe, Blizzard, EA Games), definitivamente vale a pena tentar.

Para usá-lo com linguagens .NET, o projeto LuaInterface fornecerá tudo o que você precisa.

Remo.D
fonte
1
Lua também é usada para scripts no Mod Garry's, que é um ótimo jogo :)
covarde anônimo
2

IronRuby como mencionado acima. Um projeto interessante para mim como programador C # é o suporte C # Eval em Mono . Mas ainda não está disponível (fará parte do Mono 2.2).

Borek Bernard
fonte
2

Outra votação para IronPython. Incorporá-lo é simples, a interoperação com classes .Net é direta e, bem, é Python.

Robert Rossney
fonte
1

Posso sugerir S # que mantenho atualmente. É um projeto de código aberto, escrito em C # e projetado para aplicativos .NET.

Inicialmente (2007-2009), ele estava hospedado em http://www.codeplex.com/scriptdotnet , mas recentemente foi movido para o github.

Peter
fonte
Obrigado por postar sua resposta! Certifique-se de ler as Perguntas frequentes sobre autopromoção com atenção. Observe também que é necessário que você publique um aviso de isenção de responsabilidade toda vez que criar um link para seu próprio site / produto.
Andrew Barber
Obrigado pelo conselho Andrew. Uma das respostas anteriores contém um link desatualizado para esta linguagem de script. Por algum motivo, não consigo adicionar comentário à resposta original, portanto, postei um novo para fornecer o link correto.
Peter,
1

Experimente Ela . Esta é uma linguagem funcional semelhante a Haskell e pode ser incorporada em qualquer aplicativo .Net. Mesmo ele tem um IDE simples, mas utilizável.

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

Acabei de criar um plug-in para um cliente, permitindo que escrevam código C # em módulos que agem como o VBA faz para o Office.


fonte
0

Já usei Lua antes; em um aplicativo Delphi, mas pode ser incorporado em muitas coisas. É usado no Photoshop Lightroom da Adobe .

Tony
fonte
0

Gosto de criar scripts com o próprio C # . Agora, em 2013, há um suporte muito bom para scripts C #, mais e mais bibliotecas para ele estão se tornando disponíveis.

Mono tem um ótimo suporte para script de código C # e você pode usá-lo com .NET apenas incluindo o Mono.CSharp.dllem seu aplicativo. Para aplicativo de script C # que fiz, verifique CShell

Verifique também o `ScriptEngine 'em Roslyn que é da Microsoft, mas é apenas CTP.

Como algumas pessoas já mencionaram, o CS-Script já existe há bastante tempo.

Lukebuehler
fonte