C#腳本引擎CS-Script

最近想要在程序中嵌入一個C#腳本引擎,在.NET Framework時代用過一個叫作CS-Script的東西,感受仍是不錯,發現如今也支持.NET Core了,試着嵌入一下。css

比較

要說可以運行C#腳本的解決方案,有Roslyn和Mono,與他們相比,CS-Script可以提供的封裝更爲高級,它底層是經過Roslyn之類的引擎運行的,在此基礎上,提供了一些額外功能:git

  • 執行完整的C#文件
  • 經過外部進程執行C#文件
  • 在運行過程當中連接多個c#文件,並集成運行
  • 提供簡便的方法進行連接
  • 腳本調試功能

注:因爲技術發展,不少功能可能已經被Roslyn支持了。同時基於web有Try.NETSharpLab等優秀方案。github

固然也能夠本身基於Roslyn去實現這些功能,不過CS-Script提供了更加簡單的封裝,適用於懶人。web

使用

程序基於.NET 5的開發,嘗試引用CS-Script包,發現不太好用,一直提示System.Reflection.TargetInvocationException:「Exception has been thrown by the target of an invocation.」。支持.NET Core的其實是CS-Script.Core這個包,安裝便可。shell

Install-Package CS-Script.Core

CS-Script實際上底層支持Mono/Roslyn/CodeDom三種腳本引擎,因爲.NET CORE的特殊性,CS-Script.Core作了刪減,只能支持Roslyn一種引擎了,支持的C#語言版本由Roslyn版本決定。c#

旁的不說,直接上代碼:函數

using CSScriptLib;
using System;
using System.Reflection;

namespace ConsoleApp3
{
    public class Program
    {
        static void Main(string[] args)
        {
            //var eval =  CSScript.Evaluator.ReferenceAssemblyByNamespace("System.Text");
            //var p = eval.ReferenceAssemblyByNamespace("ConsoleApp3");
            Assembly compilemethod = CSScript.RoslynEvaluator.CompileMethod(
                        @"using System;
                          public static void CompileMethod(string greeting)
                          {
                              Console.WriteLine(""CompileMethod:"" + greeting);
                          }");
            var p = compilemethod.GetType("css_root+DynamicClass");
            var me = p.GetMethod("CompileMethod");
            me.Invoke(null, new object[] { "1" });


            //eval = CSScript.Evaluator.ReferenceAssembly(sqr);
            dynamic loadmethod = CSScript.Evaluator.LoadMethod(@"using System;
                          public void LoadMethod(string greeting)
                          {
                              Console.WriteLine(""LoadMethod:"" +greeting);
                          }");
            loadmethod.LoadMethod("Hello World!");


            dynamic loadcode = CSScript.Evaluator
             .LoadCode(@"using System;
using ConsoleApp31;
using System.Text;
public class ScriptCC
{
    public void LoadCode(string greeting)
    {
        Console.WriteLine(""LoadCode:"" + greeting);
    }
}");
            loadcode.LoadCode("111");

            var eval = CSScript.Evaluator.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC);

            var ass = eval
 .CompileCode(@"using System;
public static class ScriptCCStatic
{
    public static void LoadCodeStatic(string greeting)
    {
        Console.WriteLine(""LoadCodeStatic:"" + greeting);
    }
}");
            var tp = eval.CreateDelegate(@"int Sqr(int a)
                                               {
                                                   return a * a;
                                               }");
            Console.WriteLine(tp(3));

            eval = eval.ReferenceDomainAssemblies(DomainAssemblies.AllStaticNonGAC);
            Assembly compilecode = eval
            .CompileCode(@"using System;
using ConsoleApp31;//含有這個namespace的文件包含在本項目中。
using System.Text;
using ConsoleApp3;
public class ScriptLC
{
    public void CompileCode(string greeting)
    {
        Console.WriteLine(""CompileCode:"" + greeting + Encoding.ASCII.IsBrowserDisplay);
        Program.Write();
        Test.Send();
    }
}");
            var ps = compilecode.GetType("css_root+ScriptLC");
            var obj = compilecode.CreateInstance("css_root+ScriptLC");
            var mes = ps.GetMethod("CompileCode");
            mes.Invoke(obj, new object[] { "1" });
            Console.WriteLine();

            //查看evaluator的引用程序集
            var ww = eval.GetReferencedAssemblies();
            foreach (var n in ww)
            {
                if (n.GetName().Name.Contains("System")) continue;
                if (n.GetName().Name.Contains("Microsoft")) continue;
                if (n.GetName().Name.Contains("CS")) continue;
                Console.WriteLine("AseemblyName: " + n.GetName());
                foreach (var wn in n.GetTypes())
                {
                    Console.WriteLine("Types: " + wn.Name);
                }
            }
            Console.WriteLine();

            //查看當前AppDomain加載的程序集
            foreach (var n in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (n.GetName().Name.Contains("System")) continue;
                if (n.GetName().Name.Contains("Microsoft")) continue;
                if (n.GetName().Name.Contains("CS")) continue;
                Console.WriteLine("AseemblyName: " + n.GetName());
                foreach (var wn in n.GetTypes())
                {
                    Console.WriteLine("Types: " + wn.Name);
                }
            }
            Console.ReadKey();
        }

        public static void Write()
        {
            Console.WriteLine("REFERENCE OK");
        }
    }
}

總結

使用CS-Script.Core的時候,全部加載/編譯的方法與類型都動態加入了CurrentAppDomain,能夠在主程序中進行調用(注意using和using static)。經過Evaluator.ReferenceAssembly等函數添加引用,不支持引用其餘動態編譯的代碼段。lua

能夠一次性將當前AppDomain的程序集引用加入Evaluator,可是同樣,只能調用在文件中定義的程序集,沒法加載其餘動態程序集,調用Evaluator.ReferenceDomainAssemblies(DomainAssemblies.All)將提示錯誤。spa

這個限制是Roslyn致使的,暫時沒法解決。若是須要實現多個代碼段的互相調用,能夠直接將代碼段進行拼接,或者將公用的代碼段存成文件,從文件中進行調用。.net

CompileMethod

編譯方法,並返回動態生成的程序集,方法被默認加載到DynamicClass類中,該Type徹底限定名稱爲css_root+DynamicClass,定義的靜態方法須要使用如下方式調用。

var p = compilemethod.GetType("css_root+DynamicClass");
var me = p.GetMethod("CompileMethod");
me.Invoke(null, new object[] { "1" });

LoadMethod

加載方法,並返回默認類(DynamicClass)的一個對象,經過定義返回對象爲dynamic類型,能夠直接調用實例方法。

loadmethod.LoadMethod("Hello World!");

LoadCode

加載類,並返回代碼段中的第一個類的實例,經過定義返回對象爲dynamic類型,能夠直接調用實例方法。

loadcode.LoadCode("111");

CompileCode

編譯類,並返回動態生成的程序集,定義的實例方法能夠使用如下方式調用。

var ps = compilecode.GetType("css_root+ScriptLC");
var obj = compilecode.CreateInstance("css_root+ScriptLC");
var mes = ps.GetMethod("CompileCode");
mes.Invoke(obj, new object[] { "1" });
Console.WriteLine();

CreateDelegate

生成一個委託,一樣定義在DynamicClass中,能夠直接調用。

var tp = eval.CreateDelegate(@"int Sqr(int a)
                                    {
                                        return a * a;
                                    }");
Console.WriteLine(tp(3));

參考資料

附上直接經過Roslyn使用腳本的方法:Roslyn Scripting-API-Samples.md

相關文章
相關標籤/搜索