.NET框架的核心便是通用語言運行時(CLR),顧名思義它是一個可被各種不同的編程語言所使用的運行時.CLR的很多特性可用於所有面向它的編程語言.比如,如果CLR用異常來報告錯誤,那麼所有面向它的語言都將通過異常來得到錯誤報告.如果CLR允許我們創建線程,那麼所有面向它的語言也都可以創建線程.在實際中,CLR在運行時對開發人員用何種編程語言來完成源代碼一無所知.這意味著我們應該選擇那些能夠最容易表達我們意圖的編程語言.我們可以用任何自己喜歡的語言來編寫代碼,前提是我們使用的編譯器能夠面向CLR的代碼.
針對c#: c#源代碼文件(.cs)→c#編譯器→托管模塊(IL和元數據) .最終是將源代碼編譯為托管模塊.托管模塊是一個需要CLR才能執行的標准Windows可移植執行文件(PE).
PE的各個組成部分:
①PE表頭:該表頭指出了文件類型,比如GUI和DLL,另外還包括一個時間標記用於表示文件創建日期,對於僅包含IL代碼的模塊,該表頭的大多數信息會被忽略.對於包含本地CPU代碼的模塊,該表頭還會包含有關本地CPU代碼的一些信息.
②CLR表頭:包含標識托管模塊的一些信息(可以被CLR或者一些實用工具解析).這些信息包括托管模塊所需要的CLR版本號,一些標記,托管模塊入口點方法(Main)方法的MethodDef元數據標記,以及有關模塊的元數據、資源、強命名、標記和其他一些意義不是太大的信息的位置和尺寸.
③元數據 每個托管模塊都包含一些元數據表.元數據表主要分兩種,一種用於描述源代碼中定義的類型和成員,一種用於描述源代碼中引用的類型和成員
④中間語言(IL)代碼 編譯器在編譯源代碼時產生的指令.CLR在運行時會將IL代碼編譯成本地CPU指令.
由於生存期和執行行為受CLR管理的緣故,IL代碼有時也被稱為托管代碼.實際上元數據總是和這些IL代碼一起被嵌入到同一個EXE/DLL文件中,兩者根本不能分離.因為編譯器總是同時產生元數據和IL代碼,並且總是同時將它們嵌入到生成的托管模塊中,所以元數據和它描述的IL代碼之間總能保持同步.
元數據的作用:
1.元數據省去了源代碼編譯時對頭文件和庫文件的需求,這是因為在含有實現類型和成員的IL代碼文件中,已經包含了所有被引用的類型和成員的信息.編譯器可以直接從托管模塊中讀取元數據來獲得這些信息.
2.VS可以利用元數據來輔助我們編寫代碼.它的智能感知特性就是通過分析元數據來告訴我們某個類型提供了哪些方法,以及某個方法有哪些參數.
3.CLR的代碼驗證過程可以利用元數據來確保代碼僅執行"安全"操作.
4.利用元數據,我們可以將一個對象的字段序列化到一個內存中,然後遠程傳送給另一台機器,最後再在遠程機器上執行反序列化,從而重新創建對象和它的狀態.
5.利用元數據,垃圾收集器可以追蹤對象的生存期.對於任何對象,垃圾收集器都能夠通過元數據來確定該對象的類型,並且可以獲知該對象的哪些字段引用了其他對象.
程序集
CLR其實並不和托管模塊打交道,它直接打交道的對象是程序集.程序集是一個抽象的概念,剛開始往往很難理解.首先,程序集是一個或多個托管模塊,以及一些資源文件的邏輯組合.其次,程序集是組件復用,以及實施安全策略和版本策略的最小單位.根據我們對編譯器和相關工具所做的選擇,程序集可以是一個文件或者多個文件.
當生成一個EXE程序集時,編譯器/鏈接器會產生一些特殊的信息,並將它們嵌入到結果程序集的PE文件表頭及其各個組成文件的.text部分.當EXE文件被調用時,這些特殊的信息將導致CLR被加載並初始化.CLR隨後會定位到應用程序的入口點方法,從而以此來啟動應用程序.類似地,如果一個非托管應用程序通過調用LoadLibrary來加載一個托管程序集,那麼該托管程序集DLL的入口點函數也會知道去加載CLR來處理包含其中的代碼. 加載原理如下: