.NET(C#) Internals: as a developer, .net framework in my eyes

——當我第一次聽到Microsoft .NET平臺時,我就知道它將續寫微軟不敗的神話。(Jeffrey Richter)

引言

這篇文章我很早很早之前就想寫了,本來是想把它作爲我開博的第一篇的,但由於種種原因直到現在寫出來。本文不是用.NET平臺和其餘平臺(諸如Java)做比較,不去評論孰優孰劣。僅僅是作爲一個.NET開發者,介紹一下我眼中的.NET。

1、.NET Framework

.NET Framework包括公共語言運行時(Common Language Runtime,CLR)和框架類庫(Framework Class Library,FCL)。

  1. CLR是.NET Framework 的基礎、核心,提供了包括內存管理、線程管理和遠程調用等核心服務。
  2. FCL是一個基於面向對象的可重用類型集合,用於支持多種應用的快速開發,諸如ASP.NET Web應用、Windows Form應用、Web Service應用、Windows Presentation Foundation(WPF)應用等。

.net framework in my eyes

 圖1、.NET Framework(來自:MSDN)

.NET Framework提供一個託管執行環境、簡化開發和部署、整合了多種開發語言。CLR是.NET體系的基礎,所有的.NET代碼都運行在CLR之上,並且與FCL緊密結合,並由此創建基於.NET的Windows Forms、Web Forms和XML Web Services等應用程序。.NET Framework的下層是操作系統,上層是.NET的高級開發語言(C#、F#等)。

1.1、CLR

CLR(公共語言運行時,Common Language Runtime)和Java虛擬機一樣也是一個運行時環境,它負責資源管理(內存分配和垃圾收集),並保證應用和底層操作系統之間必要的分離。

爲了提高平臺的可靠性,以及爲了達到面向事務的電子商務應用所要求的穩定性級別,CLR還要負責其他一些任務,比如監視程序的運行。按照.NET的說法,在CLR監視之下運行的程序屬於“託管”(managed)代碼,而不在CLR之下、直接在裸機上運行的應用或者組件屬於“非託管”(unmanaged)的代碼。

CLR將監視形形×××的常見編程錯誤,許多年來這些錯誤一直是軟件故障的主要根源,其中包括:訪問數組元素越界,訪問未分配的內存空間,由於數據體積過大而導致的內存溢出,等等。

可以使用 C# 語言編寫託管代碼,C# 語言提供了下列優點:

  1. 完全面向對象的設計。
  2. 非常強的類型安全。
  3. 很好地融合了 Visual Basic 的簡明性和 C++ 的強大功能。
  4. 垃圾回收。
  5. 類似於 C 和 C++ 的語法和關鍵字。
  6. 使用委託取代函數指針,從而增強了類型安全和安全性。函數指針通過 unsafe C# 關鍵字和 C# 編譯器 (Csc.exe) 的 /unsafe 選項可用於非託管代碼和數據。

1.2、FCL

.NET Framework 類庫是一個由 Microsoft .NET Framework 中包含的類、接口和值類型組成的庫。該庫提供對系統功能的訪問,是建立 .NET Framework 應用程序、組件和控件的基礎。爲便於語言之間進行交互操作,.NET Framework 類型是符合 CLS 的,並因此可在任何編程語言中使用,只要這種語言的編譯器符合公共語言規範 (CLS)。NET Framework 包括的類型執行下列功能:

  1. 表示基礎數據類型和異常。
  2. 封裝數據結構。
  3. 執行 I/O。
  4. 訪問關於加載類型的信息。
  5. 調用 .NET Framework 安全檢查。
  6. 提供數據訪問、多客戶端 GUI 和服務器控制的客戶端 GUI。

.NET Framework 提供一組豐富的接口以及抽象類和具體(非抽象)類。可以按原樣使用這些具體的類,或者在多數情況下從這些類派生您自己的類。若要使用接口的功能,既可以創建實現接口的類,也可以從某個實現接口的 .NET Framework 類中派生類。.NET Framework 類庫提供下列命名空間

2、.NET的程序運行

.NET上的程序從源碼到執行有以下幾個步驟(來自Jeffery Richter的《.NET框架程序設計》):

  1. 將源碼編譯爲託管模塊;
  2. 將託管模塊組合爲程序集;
  3. 加載公共語言運行時CLR;
  4. 執行程序集代碼。

這幾個過程我總結爲下圖:

.net framework in my eyes

圖2、.NET上的程序運行

關於託管模塊與程序集的關係,我理解如下:

CLR實際上不和託管模塊打交道,它直接打交道的對象是程序集。程序集由一個或多個託管模塊及相關的資源文件邏輯組成。其次,程序集是組件複用,以及實施安全策略和版本策略的最小單位。程序集中有一個託管模塊中包含清單(manifest)的數據塊,而清單僅僅也是一些元數據表的集合,但是這些表描述了組成程序集所有文件中的公有導出類型,以及一些和程序集相關的資源文件或數據文件。如果程序集只有一個託管模塊且沒有資源文件,該程序集就是託管模塊。

由於CLR不直接和託管模塊打交道,所以默認情況下,編譯器會將產生的託管模塊轉換爲一個程序集。只有這樣程序才能夠運行。

2.1、託管模塊

託管模塊有下列部分組成:

  1. PE32或PE32+表頭:標準PE文件頭,類似於Common Object File Format頭。如果頭使用PE32格式,文件可以運行在32位或64位的Windows當中。如果頭使用PE32+格式,文件必須運行在64位 Windows當中。該頭還指名了文件的類型:GUI、CUI或DLL,同時還包含了一個用來指明文件何時創建的時間戳。對於只包含IL代碼的模塊,PE32(+)中的大量信息都會被忽略。對於包含本地CPU代碼的模塊,頭還包括本地CPU代碼的信息。
  2. CLR表頭:包含標識託管模塊的一些信息(可以被CLR或一些工具解析)。包括託管模塊所需要的CLR版本號、一些標記、託管模塊入口點方法(main方法)的MethodDef元數據標記、以及有關託管模塊的元數據(metadata)、資源(resources)、強命名(strong name)、標記(flags)、和其他一些意義不大的信息的位置和大小。
  3. 元數據:每個託管模塊都包含一些元數據表。元數據表主要分兩種:一種用來描述代碼中定義的類型和成員。一種用來描述代碼中引用的類型和成員。
  4. IL代碼:編譯器在編譯源代碼時產生的代碼。在運行時,CLR將IL編譯爲本地CPU指令。

2.2、程序集

程序集是CLR操作的對象。程序集由一個或多個託管模塊及相關的資源文件邏輯組成。在程序集包含的所有文件中,有一個文件用於保存清單,清單是另外一組元數據表的集合,其中主要包含了程序集中的一部分文件的名稱,另外清單文件還描述了程序集的版本、語言文化、發佈者、公有導出類型、以及組成該程序集的所有文件。CLR總是先加載包含清單元數據表的文件,然後利用該清單來獲取程序集中的其它文件。

使用程序集的原因有:

  1. 程序集允許我們分離可重用類型的邏輯表示和物理表示。
  2. 可以將類型分別實現在不同的文件中,從而允許在互聯網環境中進行增量下載。
  3. 可以按需向程序集中添加資源或數據文件。
  4. 可以使我們創建的程序集包含一些用不同編程語言實現的類型。

總而言之,程序集是一個可重用、可實施版本策略和安全策略的單元。它允許我們將類型和資源劃分到不同的文件中,這樣程序集的使用者便可以決定將哪些文件打包一起部署。一旦CLR加載了程序集中包含清單的那個文件,它就可以確定程序集中的其他文件中哪些包含了程序正在引用的類型和資源。任何程序集的使用者僅需要知道包含了清單的文件名。要生成一個程序集,我們必須選擇一個託管模塊作爲清單的保存者。

PS:以上僅爲個人愚見,定有不當之處,歡迎指正!當中設計的很多概念也沒有深入,以及很多概念也沒有引出,如元數據沒有深入介紹、FCL也沒有一個系統的介紹、未介紹到應用程序域等等。當然要完成這些需要大量篇幅,以後會逐步介紹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章