[你必须知道的.NET]第十四回:认识IL代码---从开始到现在
本文將介紹以下內(nèi)容:
·???????IL代碼分析方法?
·???????IL命令解析?
·???????.NET學(xué)習(xí)方法論
1.?引言
????自從『你必須知道.NET』系列開篇以來,受到大家很多的關(guān)注和支持,給予了anytao巨大的鼓勵和動力。俱往昔,我發(fā)現(xiàn)很多的園友都把目光和焦點注意在如何理解IL代碼這個問題上。對我來說,這真是個莫大的好消息,因為很明顯我們的思路慢慢的從應(yīng)用向底層發(fā)生著轉(zhuǎn)變,技巧性的東西是一個方面的積累,底層的探索在我認為也是必不可少的修煉。如果我們選擇了來關(guān)注這項修煉,那么我們就應(yīng)該選擇如何來著手這項修煉,首先關(guān)注anytao的『你必須知道的.NET』系列可以給你提供一個捷徑,少花一些功夫;其次對大師級的作品也應(yīng)有更深入的了解,如《Applied Microsoft .NET Framework Programming》、《.NET本質(zhì)論》;再次,就是像我一樣從博客園和MSDN的知識庫中不斷的成長。呵呵,除了給自己做了個廣告之外,我認為不管是何種途徑,了解和認識IL代碼,對于我們更深刻的理解.NET和.NET應(yīng)用之上的本質(zhì)絕對有不一樣的收獲,這也就是本文研究和分享的理由。
????那么,我們要了解IL代碼,就要知道了解IL的好處,時間對每個程序設(shè)計師來說都是寶貴的,你必須清楚自己投資的價值再決定投入的資本。對于.NET程序員來說,IL代碼意味著:
·???????通用的語言基礎(chǔ)是.NET運行的基礎(chǔ),當我們對程序運行的結(jié)果有異議的時候,如何透過本質(zhì)看表面,需要我們從本質(zhì)入手來探索,這時IL是你必須知道的基礎(chǔ);
·???????元數(shù)據(jù)和IL語言是CLR的基礎(chǔ),了解必要的中間語言是深入認識CLR的捷徑;
·???????大量的事例分析是以IL來揭密的,因此了解IL是讀懂他人代碼的必備基礎(chǔ),可以給自己更多收獲。
????很明顯這些優(yōu)越性足以誘惑我們花時間和精力涉獵其中。然而,了解了IL的好處,并不意味著我們應(yīng)該過分的來關(guān)注IL,有人甚至可以洋洋灑灑的寫一堆IL代碼來實現(xiàn)一個簡單Hello world程序,但是正如我們知道的那樣,程序設(shè)計已經(jīng)走過了幾十年的發(fā)展,如果純粹的陶醉在歷史中,除了腦子不好,沒有其他的解釋。不然看見任何代碼都以IL的角度來分析,又將走進另一個誤區(qū),我們的宗旨是追求但不過分。
????因此,有了上述了應(yīng)該了解的理由和不應(yīng)該過分的基線,在擺正心態(tài)的前提下,本文開始以作者認為的方式來展開對IL代碼的認識,作者期望通過本文的闡述與分析使得大家都能對IL有個概觀之解,并在平時的項目實踐中使用這種方法通過了解自己的代碼來了解.NET。我想,這種方法應(yīng)該是值得提倡和發(fā)揮的最佳實踐,不知你信不信呢?呵呵。
2.?使用工具
????俗話說,工欲善其事,必先利其器。IL的器主要就是ILadsm.exe和reflector.exe,這兩個工具都是了解IL的基礎(chǔ),其原理都是通過反射機制來查看IL代碼。
·???????ILadsm.exe
????打開.NET Framework SKD?命令提示行,輸入ildasm回車即可打開,如圖所示:
?
??
?上圖是我們熟悉的《第十三回:從Hello, world開始認識IL》中的示例,其中的樹形符號代表的意思,可以從MSDN的一張經(jīng)典幫助示例來解釋,如下圖所示:
?
(圖表來源:MSDN)
·???????reflector.exe【下載】
??? Reflector是Lutz Roeder開發(fā)的一個讓人興奮的反編譯利器,目前的版本是Version 5.0.35.0,可以支持.NET3.0,其功能也相當強大,在使用上也較ILDASM更加靈活,如圖所示:
?
??? Reflector可以方便的反編譯為IL、C#、VB、Delphi等多種語言,是深入了解IL的最佳利器。
?
????在本文中我們以最簡單的ILadsm.exe為說明工具。
3.?分析結(jié)構(gòu)
????分析IL結(jié)構(gòu),就參閱《第十三回:從Hello, world開始認識IL》?,已經(jīng)有了大致的介紹,在此不需要進行過多的筆墨,實際上IL的本身的結(jié)構(gòu)也不是很復(fù)雜,了解了大致的體系即可。
4.?解析常用命令
????我們在了解了IL文件結(jié)構(gòu)的基礎(chǔ)上,通過學(xué)習(xí)常用的IL命令,就可以基本上對IL達到了了解不過分的標準,因此對IL常用命令的分析就是本文的重點和要點。我們通過對常用命令的解釋、示例與分析,逐步了解你陌生的語言世界原來也很簡單。
??? IL指令集包括了基礎(chǔ)指令集和對象模型指令集大概有近200多個,對我們來說消化這么多的陌生指令顯然不是明智的辦法,就行高級語言的關(guān)鍵字一樣,我們只取其一瓢獨飲,抓大放小的革命傳統(tǒng)同樣是有效的學(xué)習(xí)辦法,詳細的指令集解釋請下載[MSIL指令速查手冊]。
4.1 newobj和initobj
??? newobj和intiobj指令就像兩個兄弟,常常讓我們迷惑在其然而不知其所以然,雖然認識但是不怎么清楚,這種感覺很郁悶,下面就讓我們看看他們的究竟:
代碼引入
指令說明
深入分析
從上面的代碼中,我們可以得出哪些值得推敲的結(jié)論呢?
MSDN給出的解釋是:newobj用于分配和初始化對象;而initobj用于初始化值類型。
那么newobj又是如何分配內(nèi)存,完成對象初始化;而initobj又如何完成對值類型的初始化呢?
顯然,關(guān)于newobj指令,在《第五回:深入淺出關(guān)鍵字---把NEW說透》中,已經(jīng)有了一定的介紹,簡單說來關(guān)于newobj我們有如下結(jié)論:
·???????從托管堆分配指定類型所需要的全部內(nèi)存空間。
·???????在調(diào)用執(zhí)行構(gòu)造函數(shù)初始化之前,首先初始化對象附加成員:一個是指向該類型方法表的指針;一個是SyncBlockIndex,用于進行線程同步。所有的對象都包含這兩個附加成員,用于管理對象。
·???????最后才是調(diào)用構(gòu)造函數(shù)ctor,進行初始化操作。并返回新建對象的引用地址。
而initobj的作用又可以小結(jié)為:
·???????構(gòu)造新的值類型,完成值類型初始化。值得關(guān)注的是,這種構(gòu)造不需要調(diào)用值類型的構(gòu)造函數(shù)。具體的執(zhí)行過程呢?以上例來說,initobj MyStruct的執(zhí)行結(jié)果是,將MyStruct中的引用類型初時化為null,而基元類型則置為0。
因此,值類型的初始化可以是:
//initobj方式初始化值類型
initobj????Anytao.net.My_Must_net.IL.MyStruct
同時,也可以直接顯示調(diào)用構(gòu)造函數(shù)來完成初始化,具體為
?????MyStruct?ms =?new?MyStruct(123);
????對應(yīng)于IL則是對構(gòu)造函數(shù)cto的調(diào)用。
//調(diào)用構(gòu)造函數(shù)方式初始化值類型
call???????instance void?Anytao.net.My_Must_net.IL.MyStruct::.ctor(int32)
·???????Initobj還用于完成設(shè)定對指定存儲單元的指針置空(null)。這一操作雖不常見,但是應(yīng)該引起注意。
由此可見,newobj和initobj,都具有完成實例初始化的功能,但是針對的類型不同,執(zhí)行的過程有異。其區(qū)別主要包括:
·???????newobj用于分配和初始化對象;而initobj用于初始化值類型。因此,可以說,newobj在堆中分配內(nèi)存,并完成初始化;而initobj則是對棧上已經(jīng)分配好的內(nèi)存,進行初始化即可,因此值類型在編譯期已經(jīng)在棧上分配好了內(nèi)存。
·???????newobj在初始化過程中會調(diào)用構(gòu)造函數(shù);而initobj不會調(diào)用構(gòu)造函數(shù),而是直接對實例置空。
·???????newobj有內(nèi)存分配的過程;而initobj則只完成數(shù)據(jù)初始化操作。
關(guān)于對象的創(chuàng)建,還有其他的情況值得注意,例如:
·???????Newarr指令用來創(chuàng)建一維從零起始的數(shù)組;而多維或非從零起始的一維數(shù)組,則仍由newobj指令創(chuàng)建。
·???????String類型的創(chuàng)建由ldstr指令來完成,具體的討論我們在下文來展開。
4.2 call、callvirt和calli
call、callvirt和calli指令用于完成方法調(diào)用,這些正是我們在IL中再熟悉不過的幾個朋友。那么,同樣是作為方法調(diào)用,這幾位又有何區(qū)別呢?我們首先對其做以概括性的描述,再來通過代碼與實例,進入深入分析層面。
·???????call使用靜態(tài)調(diào)度,也就是根據(jù)引用類型的靜態(tài)類型來調(diào)度方法。
·???????callvirt使用虛擬調(diào)度,也就是根據(jù)引用類型的動態(tài)類型來調(diào)度方法;
·???????calli又稱間接調(diào)用,是通過函數(shù)指針來執(zhí)行方法調(diào)用;對應(yīng)的直接調(diào)用當然就是前面的:call和callvirt。
然而,雖然有以上的通用性結(jié)論,但是對于call和callvirt不可一概而論。call在某種情況下可以調(diào)用虛方法,而callvirt也可以調(diào)用非虛方法。具體的分析我們在以后的文章中來展開,暫不做過多分析。???
5.?結(jié)論
????本文從幾個重點的IL指令開始,力求通過對比性的分析和深入來逐步揭開IL的神秘與迷惑,正如我們在開始強調(diào)的那樣,本文只是個開始也許也是個階段,對IL的探求正如我自己的腳步一樣,也在繼續(xù)著,為的是在.NET的技術(shù)世界能夠有更多的領(lǐng)悟。作者期望通過不斷的努力逐漸和大家一起從IL世界探求.NET世界,在以后的討論中我們間或的繼續(xù)這個主題的不斷成長。
[下載]
reflector.exe
IL指令速查手冊(感謝溫少提供)
參考文獻
(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming
(USA)David Chappell,?Understanding .NET
[公告]
【CLR團隊公告】活動公告、邀請函、團隊綱領(lǐng)
【系列公告】從架構(gòu)到設(shè)計、你必須知道的.NET
溫故知新
[開篇有益]
[第一回:恩怨情仇:is和as]
[第二回:對抽象編程:接口和抽象類]
[第三回:歷史糾葛:特性和屬性]
[第四回:后來居上:class和struct]
[第五回:深入淺出關(guān)鍵字---把new說透]
[第六回:深入淺出關(guān)鍵字---base和this]
[第七回:品味類型---從通用類型系統(tǒng)開始]
[第八回:品味類型---值類型與引用類型(上)-內(nèi)存有理]
[第九回:品味類型---值類型與引用類型(中)-規(guī)則無邊]
[第十回:品味類型---值類型與引用類型(下)-應(yīng)用征途]
[第十一回:參數(shù)之惑---傳遞的藝術(shù)(上)]
[第十二回:參數(shù)之惑---傳遞的藝術(shù)(下)]
[第十三回:從Hello, world開始認識IL]
總結(jié)
以上是生活随笔為你收集整理的[你必须知道的.NET]第十四回:认识IL代码---从开始到现在的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 注意,银行推出高风险理财产品,以后的银行
- 下一篇: 注销信用卡能消除不良信用记录吗