在這個大資料/云計算/人工智能研發普及的時代,Python的崛起以及Javascript的前后端的侵略,程式員與企業似乎越來越青睞動態語言所帶來的便捷性與高效性,即使靜態語言在性能,錯誤檢查等方面的優于靜態語言,對于.NETer來說,.NET做為一門靜態語言,我們不僅要打好.NET的基本功,如基本型別/語法/底層原理/錯誤檢查等知識,也要深入理解.NET的一些高級特性,來為你的作業減輕負擔和提高代碼質量,
ok,咱們今天開始聊一聊.NET中的Emit,
一、什么是Emit?
Emit含義為發出、產生的含義,這是.NET中的一組類別庫,命名空間為System.Reflection.Emit,幾乎所有的.NET版本(Framework/Mono/NetCore)都支持Emit,可以實作用C#代碼生成代碼的類別庫
二、Emit的本質
我們知道.NET可以由各種語言進行撰寫,比如VB,C++等,當然絕大部分程式員進行.NET開發都是使用C#語言進行的,這些語言都會被各自的語言解釋器解釋為IL語言并執行,而Emit類別庫的作用就是用這些語言來撰寫生成IL語言,并交給CLR(公共語言運行時)進行執行,
我們先來看看IL語言長什么樣子:
(1) 首先我們創建一個Hello,World程式
class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } }
(2) 將程式編譯成dll檔案,我們可以看到在開發目錄下生成了bin檔案夾

(3) 向下尋找,我們可以看到dll檔案已經生成,筆者使用netcore3進行開發,故路徑為bin/Debug/netcoreapp3.0

(4) 這時候,我們就要祭出我們的il查看神器了,ildasm工具

如何找到這個工具?打開開始選單,找到Visual Studio檔案夾,打開Developer Command Prompt,在打開的命令列中鍵入ildasm回車即可,筆者使用vs2019進行演示,其它vs版本操作方法均一致


(5) 在dasm選單欄選擇檔案->打開,選擇剛剛生成的dll檔案

(6) 即可查看生成il代碼

有了ildasm的輔助,我們就能夠更好的了解IL語言以及如何撰寫IL語言,此外,Visual Studio中還有許多插件支持查看il代碼,比如JetBrains出品的Resharper插件等,如果覺得筆者方式較為麻煩可以使用以上插件查看il代碼
三、理解IL代碼
在上一章節中,我們理解了Emit的本質其實就是用C#來撰寫IL代碼,既然要撰寫IL代碼,那么我們首先要理解IL代碼是如何進行作業的,IL代碼是如何完成C#當中的順序/選擇/回圈結構的,是如何實作類的定義/欄位的定義/屬性的定義/方法的定義的,
IL代碼是一種近似于指令式的代碼語言,與匯編語言比較相近,所以習慣于寫高級語言的.NETer來說比較難以理解
讓我們來看看Hello,World程式的IL代碼:
IL_0000: nop IL_0001: ldstr "Hello World!" IL_0006: call void [System.Console]System.Console::WriteLine(string) IL_000b: nop IL_000c: ret
我們可以把IL代碼看成堆疊的運行
第一條指令,nop表示不做任何事情,表示代碼不做任何事情
第二條指令,ldstr表示將字串放入堆疊中,字串的值為“Hello,World!”
第三條指令,call表示呼叫方法,引數為呼叫方法的方法資訊,并把回傳的結構壓入堆疊中,使用的引數為之前已經入堆疊的“Hello World!”,以此類推,如果方法有n個引數,那么他就會調取堆疊中n個資料,并回傳一個結果放回堆疊中
第四條指令,nop表示不做任何事情
第五條指令,ret表示將堆疊中頂部的資料回傳,如果方法定義為void,則無回傳值
關于Hello,world程式IL的理解就說到這里,更多的指令含義讀者可以參考微軟官方檔案,筆者之后也會繼續對Emit進行講解和Emit的應用
四、用Emit類別庫撰寫IL代碼
既然IL代碼咱們理解的差不多了,咱們就開始嘗試用C#來寫IL代碼了,有了IL代碼的參考,咱們也可以依葫蘆畫瓢的把代碼寫出來了
(1) 引入Emit命名空間
using System.Reflection.Emit;
(2) 首先我們定義一個Main方法,入參無,回傳型別void
//定義方法名,回傳型別,輸入型別 var method = new DynamicMethod("Main", null, Type.EmptyTypes);
(3) 生成IL代碼
//生成IL代碼 var ilGenerator = method.GetILGenerator(); ilGenerator.Emit(OpCodes.Nop); ilGenerator.Emit(OpCodes.Ldstr,"Hello World!"); ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); //尋找Console的WriteLine方法 ilGenerator.Emit(OpCodes.Nop); ilGenerator.Emit(OpCodes.Ret);
(4) 創建委托并呼叫
//創建委托 var helloWorldMethod = method.CreateDelegate(typeof(Action)) as Action; helloWorldMethod.Invoke();
(5)運行,即輸出Hello World!
五、小結
Emit的本質是使用高級語言生成IL代碼,進而進行呼叫的的一組類別庫,依賴Emit我們可以實作用代碼生成代碼的操作,即編程語言的自舉,可以有效彌補靜態語言的靈活性的缺失,
Emit的性能非常好,除了第一次構建IL代碼所需要時間外,之后只要將操作快取在計算機記憶體中,速度與手寫代碼相差無幾
有許多著名.NET類別庫均依賴于Emit:
(.NET JSON操作庫)Json.NET/Newtonsoft.Json: github地址
(輕量ORM)Dapper:gituhb地址
(ObjectToObjectMapper)EmitMapper:github地址
(AOP庫)Castle.DynamicProxy:github地址
學習Emit:
.NET官方檔案:https://docs.microsoft.com/zh-cn/dotnet
.NET API瀏覽器:https://docs.microsoft.com/zh-cn/dotnet/api
之后作者將繼續講解.NET Emit的相關內容和應用,感謝閱讀
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/89747.html
標籤:.NET Core
上一篇:從零開始搭建前后端分離的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的專案框架之十二Swagger(引數)使用二
