由於要寫畢業論文的緣故,最近比較沒有時間寫,總是要抽出時間抽出時間。诶,這樣的生活比較煩躁。
這一篇主要寫委托、類、方法的IL代碼,一一來說明。
委托:搞過C#的都應該清楚,委托實際上是一個類。編譯器會把它編譯成一個類,繼承自MulticastDelegate的類,裡面有三個方法,BeginInvoke,EndInvoke和Invoke,當我們使用委托方法名進行調用方法時,編譯器內部實際上是調用了Invoke方法(語法糖)。
以下就用簡單的代碼來掩飾一下委托的IL代碼
先來看看編譯器把委托編譯成什麼樣子:
好啦,下面就是重要的IL代碼啦,其實大家可以先自己去嘗試解析一下,我覺得當自己去嘗試某些東西的話,會記得更牢一些。
看到上面的代碼,是否有的指令非常熟悉,我覺得大部分的指令我們在前面兩篇都有講過了,不過在這裡我還是一句一句的解釋
.method private hidebysig static void Main(string[] args)cilmanaged { .entrypoint //入口啦,這個說過很多次了。 .maxstack 2 //評估堆棧可能容納數據項的最大個數。 .locals init ( [0] class TestDemo4.Program/MyDele dele) //上面已經講過了,委托最終是編譯成類的,所以這裡是一個類類型的變量dele,存儲在調用棧。 L_0000: nop //No Operation。 L_0001: ldnull //將空引用推送到計算堆棧上。 L_0002: ldftn void TestDemo4.Program/UserInfo::PrintName(string) //將指向實現特定方法的本機代碼的非托管指針(native int類型)推送到計算堆棧上,也就是指將方法指針壓入評估棧中。 L_0008: newobj instance void TestDemo4.Program/MyDele::.ctor(object, native int)//創建委托實例並壓入評估棧中。這一步會調用委托的構造函數(.ctor),這個構造函數需要兩個參數,一個是對象引用,這裡就是L_0001:ldnull:空對象,第二個參數是方法的地址L_0002中的動作。 L_000d: stloc.0 //將評估棧中的委托實例保存到調用棧的第0個位置上。 L_000e: ldloc.0 //獲取調用棧中第0位置的值(委托實例),並壓入評估棧中。 L_000f: ldstr "Helius" //加載字符串,在托管堆中創建Helius對象,並把引用存放在評估棧上。 L_0014: callvirt instance void TestDemo4.Program/MyDele::Invoke(string) //Invoke,看到沒,委托實例調用了Invoke的的方法來執行。callvirt只能調用實例方法和虛方法,不能調用靜態方法 L_0019: nop //No Operation L_001a: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() //調用ReadKey方法。 L_001f: pop //清空評估棧。 L_0020: ret //return。 }
--------------------------------------------------------------------------------分割線------------------------------------------------------------------------------------------------
類與方法的常規代碼:
這裡先從Person類開始解析,這裡我使用到了自動屬性,大家應該也都很明了,編譯器把自動屬性編譯成了一個私有字段和兩個方法。
OK啦,又到了看Main函數的IL代碼時間了
上面的IL代碼中,只有一個指令在之前沒有出現過,那就是newobj這個指令,這個指令的意思是創建一個實例對象,並將實例對象的引用推送到計算堆棧上,也就是評估棧上。
我不想一句一句的解析了,我現在就用畫圖的形式來解釋一下實例化一個類的過程:
其實我這裡也只是泛泛之談而已,我覺得大家完全有必要自己寫一個小Demo,編譯完之後使用Reflector看一下IL代碼,這樣子會更清晰一些。而且有不懂的地方,我覺得大家完全可以自行百度或者拿出來讓大家討論一下。
下一章再講一下流程控制的IL代碼後,我想應該就可以結束掉這部分內容了。