最近想要在程序中嵌入一個C#腳本引擎,在.NET Framework時代用過一個叫作CS-Script的東西,感受仍是不錯,發現如今也支持.NET Core了,試着嵌入一下。css
要說可以運行C#腳本的解決方案,有Roslyn和Mono,與他們相比,CS-Script可以提供的封裝更爲高級,它底層是經過Roslyn之類的引擎運行的,在此基礎上,提供了一些額外功能:git
注:因爲技術發展,不少功能可能已經被Roslyn支持了。同時基於web有Try.NET和SharpLab等優秀方案。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
編譯方法,並返回動態生成的程序集,方法被默認加載到DynamicClass
類中,該Type徹底限定名稱爲css_root+DynamicClass
,定義的靜態方法須要使用如下方式調用。
var p = compilemethod.GetType("css_root+DynamicClass"); var me = p.GetMethod("CompileMethod"); me.Invoke(null, new object[] { "1" });
加載方法,並返回默認類(DynamicClass)的一個對象,經過定義返回對象爲dynamic類型,能夠直接調用實例方法。
loadmethod.LoadMethod("Hello World!");
加載類,並返回代碼段中的第一個類的實例,經過定義返回對象爲dynamic類型,能夠直接調用實例方法。
loadcode.LoadCode("111");
編譯類,並返回動態生成的程序集,定義的實例方法能夠使用如下方式調用。
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();
生成一個委託,一樣定義在DynamicClass
中,能夠直接調用。
var tp = eval.CreateDelegate(@"int Sqr(int a) { return a * a; }"); Console.WriteLine(tp(3));
附上直接經過Roslyn使用腳本的方法:Roslyn Scripting-API-Samples.md