Apa bahasa skrip terbaik untuk disematkan dalam aplikasi desktop C #? [Tutup]

98

Kami sedang menulis aplikasi desktop kaya yang kompleks dan perlu menawarkan fleksibilitas dalam format pelaporan jadi kami pikir kami hanya akan mengekspos model objek kami ke bahasa skrip. Waktu adalah ketika itu berarti VBA (yang masih merupakan opsi), tetapi kode turunan VSTA yang dikelola (saya pikir) tampaknya telah layu di pohon anggur.

Apa sekarang pilihan terbaik untuk bahasa skrip yang disematkan di Windows .NET?

Ewan Makepeace
sumber
FWIW, saya mulai dengan pendekatan dalam jawaban @ GrantPeter, menggunakan AppDomain untuk memungkinkan pembongkaran, menangani pembaruan sewa objek lintas-domain, dan mengutak-atik langkah-langkah keamanan kotak pasir. Skrip yang dikompilasi dapat memanggil metode dalam program utama, kembali melintasi batas AppDomain. Percobaan dapat ditemukan di sini: github.com/fadden/DynamicScriptSandbox
fadden

Jawaban:

24

Saya telah menggunakan CSScript dengan hasil yang luar biasa. Ini benar-benar mengurangi keharusan melakukan binding dan hal-hal tingkat rendah lainnya di aplikasi saya yang dapat ditulis.

Hector Sosa Jr.
sumber
1
Saya telah menggunakan CS-Script dalam produksi selama lebih dari setahun dan kinerjanya sangat baik.
David Robbins
Btw - untuk mengaktifkan dukungan skrip C # Anda dapat mengintegrasikan pustaka skrip C # di dalamnya, atau membuat kompilasi skrip C # sendiri. Saya telah melakukan kompilator skrip C # ringan serupa ke dalam proyek saya sendiri di sini: sourceforge.net/p/syncproj/code/HEAD/tree/CsScript.cs Kompilasi skrip C # itu sendiri agak lambat (daripada misalnya dibandingkan dengan .Lua), membuat akal untuk menghindari langkah kompilasi tambahan jika tidak diperlukan.
TarmoPikaro
114

Secara pribadi, saya akan menggunakan C # sebagai bahasa scripting. Kerangka .NET (dan Mono, terima kasih Matthew Scharley) sebenarnya menyertakan kompiler untuk setiap bahasa .NET di kerangka itu sendiri.

Pada dasarnya, ada 2 bagian dari penerapan sistem ini.

  1. Izinkan pengguna untuk mengkompilasi kode Ini relatif mudah, dan dapat dilakukan hanya dalam beberapa baris kode (meskipun Anda mungkin ingin menambahkan dialog kesalahan, yang mungkin akan menjadi beberapa lusin baris kode lagi, tergantung bagaimana dapat digunakan Anda menginginkannya).

  2. Membuat dan menggunakan kelas-kelas yang terdapat dalam rakitan terkompilasi Ini sedikit lebih sulit daripada langkah sebelumnya (memerlukan sedikit refleksi). Pada dasarnya, Anda harus memperlakukan rakitan yang dikompilasi sebagai "plug-in" untuk program tersebut. Ada cukup banyak tutorial tentang berbagai cara membuat sistem plug-in di C # (Google adalah teman Anda).

Saya telah menerapkan aplikasi "cepat" untuk mendemonstrasikan bagaimana Anda dapat mengimplementasikan sistem ini (termasuk 2 skrip yang berfungsi!). Ini adalah kode lengkap untuk aplikasi, cukup buat yang baru dan tempel kode di file "program.cs". Pada titik ini saya harus meminta maaf atas potongan besar kode yang akan saya tempel (saya tidak bermaksud untuk menjadi begitu besar, tetapi sedikit terbawa dengan komentar saya)


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

Berikan Peters
sumber
2
Apakah Anda tahu apakah ini akan berfungsi di Mono juga, atau hanya tersedia di .NET?
Matthew Scharley
4
FYI, dan bagi siapa pun yang penasaran, ya, ini dapat dikompilasi dan dijalankan di Mono dengan baik. Hanya membutuhkan referensi Sistem untuk bagian kompilasi.
Matthew Scharley
3
Melakukan ini mencemari AppDomain
Daniel Little
4
@Lavinski Jika Anda tidak ingin itu mencemari AppDomain Anda, maka cukup buat yang baru (yang mungkin merupakan ide yang baik sehingga Anda dapat menempatkan keamanan yang lebih ketat pada "skrip")
Grant Peters
3
@Lander - Ini sangat ringan. Kode di atas adalah keseluruhan program yang mendukung "scripting". csscript.netSepertinya itu semacam pembungkus untuk ini. Ini pada dasarnya adalah implementasi tanpa tulang. Saya pikir pertanyaan yang lebih baik adalah "apa yang csscript.net lakukan untuk saya yang tidak". Saya tidak tahu apa yang dilakukan csscript, tetapi mereka pasti tahu apa yang dilakukan kode di atas di sini, mereka memilikinya (atau sesuatu yang sangat mirip) di perpustakaan mereka.
Grant Peters
19

Mesin PowerShell dirancang agar mudah disematkan dalam aplikasi agar dapat ditulis. Faktanya, PowerShell CLI hanyalah antarmuka berbasis teks ke mesin.

Edit: Lihat https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/

Rodrick Chapman
sumber
Saya rasa ini adalah solusi terbaik karena tidak menambahkan objek ke AppDomain. Di .Net Anda tidak pernah dapat membongkar rakitan atau kelas.
Daniel Little
12

Bahasa hinaan .

jop
sumber
1
Tampak mati jika itu proyek di github.com/boo-lang/boo
kristianp
@kristianp saat ini, ada beberapa commit setiap bulan pada project (hingga sebulan yang lalu). Jadi mungkin sudah kehilangan sedikit daya tarik, tapi sepertinya masih hidup.
Tamás Szelei
8

Bahasa skrip pilihan saya adalah Lua hari ini. Kecil, cepat, bersih, terdokumentasi lengkap, didukung dengan baik, memiliki komunitas yang hebat , digunakan oleh banyak perusahaan besar di industri (Adobe, Blizzard, EA Games), pasti patut dicoba.

Untuk menggunakannya dengan bahasa .NET, proyek LuaInterface akan menyediakan semua yang Anda butuhkan.

Remo.D
sumber
1
Lua juga digunakan untuk membuat skrip di Garry's Mod, yang merupakan permainan hebat :)
pengecut tanpa nama
2

IronRuby seperti yang disebutkan di atas. Proyek yang menarik bagi saya sebagai programmer C # adalah dukungan C # Eval di Mono . Tapi itu belum tersedia (akan menjadi bagian dari Mono 2.2).

Borek Bernard
sumber
2

Suara lain untuk IronPython. Menyematkan itu sederhana, interoperasi dengan kelas .Net sangat mudah, dan, yah, itu Python.

Robert Rossney
sumber
1

Saya mungkin menyarankan S # yang saat ini saya pertahankan. Ini adalah proyek open source, ditulis dalam C # dan dirancang untuk aplikasi .NET.

Awalnya (2007-2009) dihosting di http://www.codeplex.com/scriptdotnet , tetapi baru-baru ini dipindahkan ke github.

Peter
sumber
Terima kasih telah memposting jawaban Anda! Harap pastikan untuk membaca FAQ tentang Promosi Mandiri dengan cermat. Perhatikan juga bahwa Anda diharuskan memposting disclaimer setiap kali Anda menautkan ke situs / produk Anda sendiri.
Andrew Barber
Terima kasih atas saran Andrew. Salah satu jawaban sebelumnya berisi tautan usang ke bahasa skrip ini. Untuk beberapa alasan saya tidak dapat menambahkan komentar ke jawaban asli, jadi saya telah memposting yang baru untuk memberikan tautan yang benar.
Peter
1

Coba Ela . Ini adalah bahasa fungsional yang mirip dengan Haskell dan dapat disematkan ke aplikasi .Net apa pun. Meskipun memiliki IDE yang sederhana namun dapat digunakan.

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

Saya baru saja membuat plugin untuk klien, memungkinkan mereka menulis kode C # dalam modul yang berfungsi seperti yang dilakukan VBA untuk Office.


sumber
0

Saya telah menggunakan Lua sebelumnya; di aplikasi Delphi tetapi dapat disematkan di banyak hal. Ini digunakan di Adobe Photoshop Lightroom .

Tony
sumber
0

Saya suka membuat skrip dengan C # itu sendiri . Sekarang, pada tahun 2013, ada dukungan yang cukup baik untuk C # scripting, semakin banyak pustaka yang tersedia.

Mono memiliki dukungan yang bagus untuk skrip kode C # , dan Anda dapat menggunakannya dengan .NET hanya dengan memasukkan Mono.CSharp.dlldalam aplikasi Anda. Untuk aplikasi scripting C # yang saya buat, periksa CShell

Lihat juga 'ScriptEngine' di Roslyn yang berasal dari Microsoft, tetapi ini hanya CTP.

Seperti yang telah disebutkan beberapa orang, CS-Script telah ada sejak lama juga.

lukebuehler
sumber