在開發領域,我們經常會遇到需要動态加載和執行代碼的場景。對于Python、JavaScript、Lua等腳本語言,動态性是它們的天性,而對于需要預先編譯的語言,如C#,動态執行似乎并不那麼直覺。但随着AI的普及,例如我們想在C#程式中動态執行AI生成的代碼段,這就要求我們能在運作時編譯和執行C#代碼。接下來,讓我為你介紹一個強大的架構——Natasha。
Natasha:動态地建構你的.Net世界
Natasha是一個基于Roslyn的C#動态程式集建構庫。它允許開發者在程式運作時動态地建構域、程式集、類、結構體、枚舉、接口和方法等。這意味着開發者可以在不停止應用程式的情況下,為其增加新的程式集。
Natasha架構具備域管理和插件管理功能,支援域的隔離和解除安裝,實作了熱插拔。它提供了完善的錯誤提示,自動添加引用,并且擁有完整的資料結構構模組化闆,進而讓開發者專注于程式集腳本的編寫。更何況它還有着跨平台的優勢,并且對netcoreapp2.0+ / netcoreapp3.0+都相容。
Natasha的使用場景
你可能會好奇,這樣一個動态編譯庫是如何彰顯其價值的?其實,動态編譯技術是支撐如今.NET生态不可或缺的重要部分。無論是在官方還是非官方的庫中,動态編譯的技術都扮演着“服務”的角色。其核心是MSIL,官方為我們提供了Emit技術來編寫IL代碼。但Emit的編寫和維護并不友好,給開發者帶來了諸多挑戰。
Roslyn的出現仿佛開啟了新世界的大門,它使得Emit變得透明,并允許我們直接用C#進行動态編譯。Natasha就是在這樣的基礎上發展起來的,經過精心設計與不斷疊代,它正成為動态編譯領域的佼佼者。
Natasha的簡單應用場景
借助Natasha,你可以實作衆多有趣而實用的功能,如建立AOP代理類或動态建構Controller來實作動态API,甚至在程式啟動時與CodeFirst相容的ORM一起使用,動态建立表結構,甚至通過動态執行AI建立的代碼段,這是個非常有趣的事情!
不可否認,這些功能的實作需要一定的程式設計基礎。例如,下面的代碼展示了如何使用Natasha快速開始一個域,并利用其插件管理功能。
// 開始建立域 var domain = new NatashaDomain(); // 建立非主域 var domain = new NatashaDomain(key); // 加載插件 var assembly = domain.LoadPlugin(pluginPath); // 使用程式集比較器 domain.SetAssemblyLoadBehavior(AssemblyCompareInfomation.UseHighVersion); // 封裝API domain.LoadPluginWithHighDependency(PluginPath);
在智能編譯模式下,你可以使用如下代碼快速地進行編譯,Natasha将智能地合并中繼資料和Using聲明,并進行語義檢查。
AssemblyCSharpBuilder builder = new AssemblyCSharpBuilder(); var myAssembly = builder.UseRandomDomain() .UseSmartMode() .Add("public class A{ }") .GetAssembly();
Natasha的輕便編譯模式
對于更加輕便的編譯需求,Natasha提供了簡潔編譯模式。該模式會合并目前域的中繼資料和Using聲明,并關閉語義檢查,提供一種更加靈活快速的編譯方式。
AssemblyCSharpBuilder builder = new AssemblyCSharpBuilder(); var myAssembly = builder.UseRandomDomain() .UseSimpleMode() .Add("public class A{ }") .GetAssembly();
中繼資料管理與微調
Natasha也提供了完整覆寫和部分覆寫引用和using代碼的能力。例如,合并共享域的引用和using代碼可以使用以下方法:
builder.WithCombineReferences(item => item.UseAllReferences()); builder.WithCombineUsingCode(UsingLoadBehavior.WithAll);
如果希望合并目前域的引用和using代碼或者使用自定義的引用,可以使用如下方法:
builder.WithCurrentReferences(); builder.WithCombineUsingCode(UsingLoadBehavior.WithCurrent); // 使用自定義的中繼資料引用 builder.WithSpecifiedReferences(someMetadataReferences);
腳本教程
對于編寫和加載腳本,Natasha采用靈活的配置API來覆寫using代碼,并添加編譯選項。這允許開發者指定腳本中要使用的C#語言版本,以及如何處理using指令。
// 配置語言版本 builder.ConfigSyntaxOptions(opt => opt.WithLanguageVersion(LanguageVersion.CSharp6)); // 添加腳本并覆寫Using Code builder.WithCombineUsingCode(UsingLoadBehavior.WithAll).Add(myCode); // 自定義覆寫Using Code builder.Add("script", UsingLoadBehavior.WithCurrent);
進階編譯技巧
Natasha提供了一系列的With、Set和Config系列API來精細控制編譯過程。你可以配置編譯選項、診斷資訊級别,甚至啟用或關閉某些特殊的編譯行為。例如,啟用語義檢查或添加語義處理插件:
// 啟用語義檢查 builder.WithSemanticCheck(); //增加語義處理插件 builder.AddSemanticAnalysistor();
動态調試
使用Natasha進行動态源代碼調試是輕而易舉的。開啟調試模式可以幫助你更深入地了解代碼執行情況,Natasha提供了多種選項來寫入調試資訊:
builder.WithDebugCompile(item => item.WriteToFile()); // 調試資訊寫入檔案 builder.WithDebugCompile(item => item.WriteToAssembly()); // 調試資訊整合到程式集 builder.WithReleaseCompile(); // 設定為Release模式
生成程式集
在程式集被編譯前,你可以使用Natasha提供的API來進行各種配置,比如設定程式集名稱或輸出選項:
builder.SetAssemblyName("MyAssembly"); builder.WithSemanticCheck(); // 啟用語義檢查 builder.WithFileOutput("path/to/dll", "path/to/pdb", "path/to/xml"); // 檔案輸出配置
使用 Codecov 的擴充
Natasha還提供了一個Codecov擴充,可幫助你擷取代碼覆寫率資料。首先你需要引入
DotNetCore.Natasha.CSharp.Extension.Codecov
擴充包,然後像下面這樣使用:
builder.WithCodecov(); Assembly asm = builder.GetAssembly(); List<(string MethodName, bool[] Usage)>? coverageData = asm.GetCodecovCollection(); Show(coverageData);
上面的
Show
方法将周遊并顯示每個方法的執行情況。這是一種很好的方式來監測你的代碼如何執行,確定品質和可靠性。
Natasha 的類型擴充
最後,Natasha提供了類型擴充來幫助你更輕松地處理類型資訊。例如,擷取運作時或開發時的類型名稱,或者檢查類型是否實作了某個接口:
typeof(Dictionary<string,List<int>>[]).GetRuntimeName(); // 擷取運作時類型名稱 typeof(Dictionary<string,List<int>>).IsImplementFrom<IDictionary>(); // 檢查是否實作指定接口
當然這個項目也是開源的,不論是學習思路還是代碼設計方案 ,檢視下面的項目位址都是不錯的選擇
https://github.com/dotnetcore/Natasha
後面我會使用Natasha嘗試通過AI來生成c#代碼并動态執行,可以關注我,并持續關注我的下一步行動!