動態編譯庫 Natasha 5.0 于十月份發布,此次大版本更新帶來了強大的兼容性支持,目前 Natasha 已支持 .NET Standard 2.0 及 .NET Core 3.1 以上版本(包括 .NET Framework)了。
引入項目
NuGet\Install-Package?DotNetCore.Natasha.CSharp?-Version?5.0.0
引擎分離
從 Natasha 5.0 開始,將支持根據?TargetFramework?目標版本來適配對外 APIs,為開發者自動選擇提供單域編譯引擎或多域編譯引擎。
單域編譯引擎
支持 .NET Standard 2.0(.NET Core 3.1 以下,以及.NET Framework)版本
動態構建工作將在主域 Main Domain 中進行
不具備多域編譯引擎帶來的優勢,也無法卸載動態編譯輸出的程序集
簡化了部分 APIs,移除一部分不必要的 APIs,不兼容上一版本的 Natasha APIs
多域編譯引擎
兼容 .NET Core 3.1 及以上版本(包括 .NET Core 和 .NET 5+)
支持程序集卸載 Assembly Uninstall
支持域功能隔離 Domain Functions Isolation
支持插件加載與卸載 Plugin Load & Unload
兼容上一版本的全部 Natasha APIs
代碼分離
Natasha 5.0 在源碼層面分為?MultiDomain、Public?和?SingleDomain?三部分,并使用自定義宏?MULTI?來區分單域與多域。
Natasha 5.0 從工程文件層面上做了兼容性隔離,確保 Natasha 在后續版本的升級中不必過多關注兼容性問題,多域編譯引擎依舊是 Natasha 未來版本的主要方向,后續的升級與優化工作也將在?MutliDomain?中開展。
如多域編譯引擎提供了如下接口:
{OperatorClass}.DefaultDomain/CreateDomain/RandomDomain/UseDomain
其對應的單域編譯引擎僅支持如下接口:
{OperatorClass}.DefaultDomain
單域編譯引擎的編譯結果會全部加載到主域當中,因此不具備域功能隔離與卸載能力。
使用須知
編譯前提
使用?字符串腳本?需要對編譯原理有一定程度的了解。Natasha 與 Roslyn 將為開發者極大地簡化了復雜的理論依據和構建過程。開發者在使用 Natasha 時需要關注以下問題:
一、元數據管理問題
熟悉?Emit/Expression?的開發者了解,在構建過程中可能會使用到反射(如?PropertyInfo、FieldInfo、MethodInfo?等信息)。由于許多開發者在編寫代碼時,只關注反射的使用,忽視了這些元數據對動態編譯的重要性,因此在通過字符串腳本進行編譯時,時常會出現各種問題。
Natasha 和 Roslyn 同樣需要元數據,而元數據的來源十分多樣,如引用程序集、內存程序集、實際程序集等。其中,除內存程序集外,元數據均被記錄在 DLL 文件中,因此當使用如下構建代碼時:
NatashaManagement.AddGlobalReference("1.dll");
可能會引發如下異常:
找不到 RuntimeMetadataVersion 的值。找不到包含 System.Object 的程序集,或未通過選項為 RuntimeMetadataVersion 指定值。
引用管理對于程序來講是具有一定負擔的,因為目前還不能從內存程序集中提取到元數據,所以需要以文件的方式來添加,這也就導致了開發者在發布動態編譯程序時,需要有完畢的引用文件跟隨 —— 將導致所發布的包體積變大。至于環境需要哪些引用文件,可以交由?NCC 編譯環境包來解決。如果開發者不能很好地管理引用,那么直接引入該包來全面覆蓋當前程序的元數據是最好的選擇。
二、Using 管理問題
Using 的管理關乎元數據的引用來源。任何一個動態構建,都是以一個完整類的方式進行的,因此完整的類?using?代碼是必不可少的。Natasha 構建模板目前可以覆蓋大部分?using,并能通過語義過濾來處理異常?using。如果開發者直接使用?AssemblyCSharpBuilder?來構建代碼,那么就需要注意腳本中的?using?問題。
編譯環境
編譯環境包目前已不再包含于 Natasha Package 中,因此需要使用以下方法來加載編譯環境:
使用 Natasha 接口來管理全局引用與 Using 緩存
NatashaManagement.AddGlobalReference/AddGlobalUsing
使用 NCC 編譯環境包來解決元信息的引用問題
DotNetCore.Compile.Environment
輸出環境
如果開發者認為生成的文件中有太多的多語言適配資源,那么可以使用?SatelliteResourceLanguages?來指定默認的資源語言:
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
二義性錯誤
二義性問題仍被歸屬到開發者的錯誤開發行為中,不應由 IDE 或 Natasha 來解決。Natasha 團隊傾向于在命名空間發生沖突時,由開發者手工解決該問題,Natasha 的上下文語義環境并不能百分之百地推斷出用戶究竟想用哪一個命名空間。
目前有三種解決方案可供開發者選擇:
使用?
Natasha.CSharp.Extension.Ambiguity
?擴展包,通過?.Using()?或?.ConfigUsing()?模板自帶的方法來指定優先級最高的?using?。該包將在不久之后將獨立并進入 Natasha 生態社區 NMS 之中;直接使用引擎?
AssemblyCSharpBuilder
?編譯字符串腳本,在字符串層面進行替換;定制語義過濾方法,更新編譯單元中的語法樹,使用 Natasha 的語義擴展方法,將自定義語義過濾方法添加到 Natasha 中:
assemblyCSharpBuilder.AddSemanticAnalysistor(Func<AssembltCSharpBuilder,?CSharpCompilation,?CSharpCompilation>)
(需要有語法語義相關開發經驗)
示例
一個盡可能復雜的示例:
var?action?=?NDelegate//使用隨機域?也可以使用?CreateDomain?/?UseDomain?/?DefaultDomain?
//Core3.1以下僅能使用?DefaultDomain
.DefaultDomain()//[可選API]?必要時使用?ConfigBuilder?配置編譯單元(下面只為展示API,?有需求就用,?沒需求不用寫)
.ConfigBuilder(builder?=>?builder//配置編譯器選項.ConfigCompilerOption(opt?=>?opt//配置平臺.SetPlatform(Microsoft.CodeAnalysis.Platform.AnyCpu)//Release?方式編譯.CompileAsRelease()//開啟可空警告.SetNullableCompile(Microsoft.CodeAnalysis.NullableContextOptions.Warnings))//配置語法選項.ConfigSyntaxOptions(opt?=>?opt//配置支持的腳本語言版本.WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8))//禁用語義檢查與過濾.DisableSemanticCheck()
)//[可選API]?配置該方法所在的類模板
.ConfigClass(item?=>?item//給類配置一個名字,不用隨即名.Name("myClass")//不使用默認域的?Using?緩存.NoGlobalUsing())//[可選API]?為類模板添加?using?引用
.ConfigUsing("System")//這里的?API?參照定義的委托,?包括委托的參數//例如?Action<int>?/?Func<int,int>?擁有一個參數,?參數的名字請在?Action<int>?/?Func<int,int>?上?F12?查看定義獲取參數名.
.Action("Console.WriteLine(\"Hello?World!\");");action();?/*Output:?Hello?World!*/
更新日志
2022/09/05 - 2022/09/21
分離引擎,項目分為多域和單域以部分類方式合并 APIs
使用?IndexOf?替代?Contans?方法做兼容
支持 .NET Standard 2.0、.NET Core 3.1+ 以及 .NET 5+ 版本
升級?
DotNetCore.SourceLink.Environment
?依賴,支持 .NET Standard 2.0/2.1 版本升級?
DotNetCore.Compile.Environment
?依賴,支持 .NET Standard 2.0/2.1 版本
2022/09/30 - 2022/10/09
使用?Assembly.ReflectionOnlyLoad?替代?MetadataLoadContext?解決單域引擎只讀元數據的問題
優化單域引擎初始化過程中掃描源 DLL 文件的問題