人老了,玩不轉博客園的編輯器,詳細信息轉到:https://mp.weixin.qq.com/s/1r6YKBkyovQSMUgfm_VxBg 關鍵字:Github, NCC, Natasha,Roslyn, .NET Core2.0,.NET Core2.1,.NET Core2.2,.NET C ...
人老了,玩不轉博客園的編輯器,詳細信息轉到:https://mp.weixin.qq.com/s/1r6YKBkyovQSMUgfm_VxBg
關鍵字:Github, NCC, Natasha,Roslyn, .NET Core2.0,.NET Core2.1,.NET Core2.2,.NET Core2.3, standard2.0, 動態編譯,運行時腳本,高性能。
一、 前言
對於開源貢獻者,Emit和表達式樹不是陌生的字眼,IL的動態特性為封裝工作帶來了極大的方便,會Emit的開發者可以說駕馭了大部分的高性能、高動態的編程技巧。縱觀ef、dapper、json.net等第三方常用庫,哪個能脫離emit而獨善其身,也正因如此,幸福了一批批懶癌患者,包括我這個懶癌中晚期患者(這裡給各位病友問好),與此同時本人對封裝有著莫名其妙的執念,就在兩支怪力的驅使下走上了對emit的不歸路.
舊版Natasha始於2016年,當時是對Emit進行的封裝,中途經有檸檬的提醒完善了UT和相容性等工作,後由Victor.X.Qu補充了文檔,後經ORM實戰。
二、Emit非銀彈
經歷過重重思考和實踐,Emit不是動態的最佳實踐,簡單的從以下幾個角度來講:
-
調優:
-
dup : emit中的dup指令優化在是由開發者控制的,在熟悉指令操作的同時又給開發者帶來了額外的優化工作。
-
if/while/for :不得不說IL可以透過代碼看本質,指令就是這樣的,在條件分支上,標簽跳轉的形式使得邏輯執行靈活多變。這樣除了棧的操作之外,還要關註標簽的位置和跳轉語句的優化,另外還要清晰的記得你的各個分支。
-
併發字典與演算法優化 :這一點是出自我的極端,在對象成員的賦值/載入等操作面前,併發字典像是一場災難,賣盡氣力優化的動態執行,卻被某些數據結構所糟蹋。至於演算法與動態編譯結合起來,應該沒幾個病友做過,各位如果有興趣的話可以慢慢體會。
-
相容性:
-
結構體 : 類與結構體在操作指令上有著諸多的不同,開發者不僅僅要熟悉對類的操作指令,還要對結構體做出相容,諸如ldflda、 ldloca、Constrained等指令,對於開發者來說並不是一件省心的事。
-
類型轉換 : .NET中的類型轉換不僅僅有指令級的轉換,standard還提供了諸多方法支持不同類型之間的轉換,因此你還需要花一些功夫去處理這些。
-
語法糖 : 一切語法糖在emit面前都要還原,比如可空類型語法糖,對象比較語法糖,類型比較語法糖等等,無疑會大大增加相容工作的負擔(core3.0的可空引用我還沒有做測試)。
-
構建難度:
-
深度克隆 : 深度克隆是動態編程的一個典型實戰,如果各位病友堅持用EMIT挑戰的話,可以沒病走兩步,走兩步。
-
深度構建 :一旦遇到了動態構建動態場景,那麼這個複雜度難以想象。
-
猜錯誤 : Emit並沒有很好的友情提示,沒有語法檢查,而被程式鍛煉成老獵手一定要付出很多代價。
-
維護升級:
-
後續開發 :接手emit代碼是一件令人糾結的事,當量變引起質變的時候,從興奮到苦不堪言這種事情並不是沒有發生過,尤其是現在.NET開源工作者都比較獨立,沒有凝聚力和氛圍,人的生命以及精力是有限的。
-
傳承 :由上面諸多信息也可見,在新技術的衝擊下,在令人不安的環境下,在孤獨的夜裡,傳承也是個問題。
儘管表達式樹已經幫我們做了一些工作,但複雜場景和使用習慣仍然封印著開發者的大腦。
三、狙擊暴君
Roslyn到如今已經耳熟能詳了,編譯被當作成服務對外開放,讓不少開發者從中受益,但由於文檔不全,實例不充分,從開始一直到2018年期間,對於懶癌開發者來說,基於Roslyn開發都是一件憋手的事情(例如一些必備操作文檔,在2019年今年5月份才提上日程)。Natasha使用Roslyn做為編譯引擎,不僅僅在動態構建上進行了人性化升級,還在功能上進行了簡化。您不僅可以使用Natasha輕鬆的構建類、結構體、方法、介面、抽象類,還可以輕鬆的繼承類、重載方法、實現介面、抽象類等等,技術較新,僅支持.standard2.0。
項 目 地 址:https://github.com/dotnetcore/Natasha
Nuget索引:DotNetCore.Natasha (正式版1.0.0.0)
(娜塔莎)(原型蘇聯紅軍第25步兵師的中尉柳德米拉·帕夫利琴科,一名出色的女狙擊手)
使用Natasha你需要關註:
-
在您的工程文件里添加這個節點:
<PreserveCompilationContext>true</PreserveCompilationContext>
-
瞭解wiki中反解器的概念及使用。
-
註意命名空間,自動補充命名空間目前尚未支持,需要您手動操作,使用using方法添加。
-
想盡一切辦法拼接字元串,目前符合CSharp7.3或以下C#版本的都行。
-
編譯模式有區分:StreamComplier記憶體流編譯/FileComplier文件流編譯, 文件流編譯的內容,可以被動態調用。當你想動態編譯類B的時候使用類A,那類A就需要使用文件流編譯,相當於dll動態載入到運行時。
-
使用Natasha中的Operator來構建你的動態內容。
四、性能
這幾年隨著.NET架構引擎的不斷升級,dynamic、emit執行性能已經得到了大幅度提升,roslyn也不例外,之前官方給過性能測試截圖,上面顯示是比emit快一點,個人的基準測試要等下一個benchmark版本,從耗時的角度來說roslyn <= emit (roslyn有指定release模式編譯),所以大家根本不用關心性能問題。
五、使用案例