在開發領域,我們經常會遇到需要動態載入和執行程式碼的場景。對於 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#程式碼並動態執行,可以關註我,並持續關註我的下一步行動!