.NET面試題系列[2] - .NET框架基礎知識(2)


3 程序集

面試出現頻率:雖然很重要但不怎么出現,可能會考你定義,以及程序集包括什么,然后自然的話題就跑到反射上去了。

重要程度:8/10,很重要

需要理解的程度:知道程序集包括IL和元數據。知道元數據的作用以及反射的概念。知道GAC是什么。關於反射在后面另有獨立章節。對於程序集的強命名,個人認為過於偏僻。

 

3.1 概念

程序集構成了基於.NET的應用程序的部署、版本控制、重用和安全權限的基本單元。程序集以可執行 (.exe) 文件或動態鏈接庫 (.dll) 文件的形式出現。它們向公共語言運行時提供了解類型實現所需要的信息。可以將程序集看成是構成邏輯功能單元並為一起工作而生成的類型和資源的集合。

如果程序集中含有多個命名空間,則每個命名空間有自己的IL和元數據(即托管模塊)。多個托管模塊合成一個程序集。CLR是和程序集一起工作的,而不是和托管模塊一起。

如果你的程序只是Hello World級的小控制台應用程序,那么編譯之后,可能你只會用到.NET最主要的基礎類庫mscorlib.dll(最重要的程序集之一)。但對於團隊級的系統來說,可能會有大量dll文件作為類庫。如果你在VS中選擇新建一個Class Library,則編譯后生成的結果文件是dll文件,沒有可執行程序,你也不能在VS中試圖運行一個Class Library。

不同程序集中相同的命名空間中相同的成員(例如類型)被認為是不同的。例如My.dll和Your.dll同時在一個命名空間A中定義了一個類B,則它們是不同的。

程序集是自描述的:它的清單部分含有它需要訪問的其他程序集(依賴對象)名單,它的元數據包含了程序集中所有類型以及它們的成員。它的IL代碼則包括了成員的實現。

程序集是可配置的:可以將其配置到私有或共享(全局程序集緩存,GAC)中。當你在一個類庫中引用其他程序集(通過Add References)時,系統將該程序集的dll文件拷貝到你的類庫的子目錄bin\Debug下(這就是私有配置)。注意Add References不會顯示GAC中的程序集。全局的程序集不需要Add References,IDE自動添加。配置到GAC的步驟是一個很偏僻的話題,可參考https://msdn.microsoft.com/zh-cn/library/yf1d93sz.aspx

 

3.2 程序集的結構

程序集最重要的兩部分是IL和元數據。它們合稱托管模塊。程序集包括以下部分:

  1. PE/COFF頭:包含了供操作系統查看和利用的信息。Windows操作系統能夠加載並運行.dll和.exe是因為它能夠理解PE/COFF文件的格式。
  2. CLR頭:告訴操作系統這個PE/COFF文件是一個.NET程序集,區別於其他類型的可執行程序。程序集中包含的IL語言代碼並不是計算機可以直接執行的,還需要進行即時編譯,那么在對IL語言代碼進行編譯前,需要先將編譯的環境運行起來。
  3. 清單(manifest):相當於一個目錄,描述了程序集本身的信息,例如程序集標識(名稱、版本、文化)、程序集包含的資源(Resources)、組成程序集的文件、該程序集需要用到的所有外部程序集名單等。
  4. 元數據:如果說清單描述了程序集自身的信息,那么元數據則描述了程序集所包含的內容。這些內容包括:程序集包含的模塊、類型、類型的成員、類型和類型成員的可見性等。注意,元數據並不包含類型的實現,有點類似於C++中的.h頭文件。.NET中,查看元數據的過程叫做反射(Reflection)。
  5. IL:也就是元數據中類型的實現,包括方法、屬性等。
  6. 資源文件: 例如圖標文件,文本文件,.resx資源文件等。

 

3.3 元數據的作用

部分元數據的作用:

  • IDE通過元數據進行智能感知,例如在你打出一個.之后,自動彈出下拉菜單,獲得類型的方法和屬性等。
  • CLR的代碼驗證過程使用元數據確保代碼只執行類型安全的操作。
  • 序列化和反序列化的基礎。
  • 通過訪問元數據來獲得類型的成員(即反射)。雖然這會降低性能,但很多時候必須要這么做,例如類型是動態類型,ORM框架即為一個常見的場景。

 

3.4 程序集和命名空間有何區別?

命名空間是一個程序集內相關類型的一個分組。例如System.IO命名空間包含了有關文件IO的類型。有時,多個程序文件可能共享一個命名空間。例如如果你開發一組幾何類圓圈,三角和正方形,你可以將他們的命名空間都設為“Shapes”。

命名空間可以嵌套。例如namespace System.IO等同於

namespace System{

  namespace IO{                                     

    …

  }

}

一個程序集可以包括多個命名空間。在不同程序集中相同名字的命名空間是不同的兩個對象。程序集和命名空間的主要區別:

  1. 程序集是部署,重用應用程序的最小單位,但命名空間不是,它更多的是將具有相似內容的一組類型和方法組織到一起。例如mscorlib.dll中的System命名空間,包含了.NET所有的基元類型。
  2. 一個程序集可以包括多個命名空間,反之則不行
  3. Using引用的對象是命名空間,而不能是程序集。你不能using mscorlib.dll。但當你using 例如System.Data(這是一個嵌套的命名空間)時,你可以使用System.Data命名空間的所有可訪問類,屬性及方法,就像其代碼是你的一部分一樣。

 

3.5 什么是GAC?

當你安裝了CLR,你就有了一個Global Assembly Cache(全局程序集緩存,GAC)。安裝CLR時,系統將把它認為重要的若干程序集放入GAC,例如mscorlib.dll。從 .NET Framework 4 開始,全局程序集緩存的默認位置為 %windir%\Microsoft.NET\assembly。 在 .NET Framework 的早期版本中,默認位置為 %windir%\assembly。

有時候當安裝某些應用程序時,也會觸發安裝程序將程序集放入GAC。

GAC是一個機器級別的程序集,其中包括mscorlib.dll等至關重要的程序集。在Add Reference中,它不會被自動包括進來,必須手動瀏覽才可以找到部署到GAC中的程序集。如果你打算將類庫部署到GAC,一般來說,這個庫應當被大量其他工程引用。

不能把可執行的程序集部署到GAC。部署到GAC的細節,參閱精通C#第14章以及https://msdn.microsoft.com/zh-cn/library/yf1d93sz.aspx。在全局程序集緩存中部署的程序集必須具有強名稱。將一個程序集添加到全局程序集緩存時,必須對構成該程序集的所有文件執行完整性檢查。

 

4 綜合問題

題目:hello world程序。

 1 using System;
 2  
 3 class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             string text = "hello, world!";
 8             Console.WriteLine(text);
 9         }
10     }
View Code

 

問:該程序需要引入什么參考?

答:什么都不需要。

問:也就是說你可以把VS幫你引用的所有參考都刪了?

答:是。這個程序只需要基礎類庫。

問:那你都刪了之后,Console類型從哪里來?

答:從mscorlib.dll里來。另外,string這個類型也從那兒來,因為string是基元類型,所有的基元類型都在mscorlib.dll的System命名空間。所以你不能把第一行那個using拿掉。

問:為什么我從來沒見過mscorlib.dll?

答:因為它在GAC里,每次自動引用。

問:如果我用VS編程,運行程序(非調試模式),會發生什么?

答:VS會先用C#編譯器將源代碼編譯為一個程序集。程序集包括IL代碼。因為源代碼沒問題,所以編譯成功,之后,CLR引用程序集中所有需要的其他程序集(這個例子就是沒有其他程序集),進行運行時檢查,檢查也沒問題,就開始調用JIT進行即時編譯。將IL轉換為機器碼。機器運行機器碼,打印出hello, world!,然后退出程序。

問:你剛剛提到了程序集,那是作什么用的?

答:程序集是部署和重用應用程序的最小單元。它是自解釋的,主要包括IL和元數據,以及資源文件等。

問:你接觸過或者對程序集進行過訪問嗎?

答:在反射時會訪問程序集中的元數據。

問:反射有什么用處?它對性能是否有影響?

答:且聽以后分解。

 

5 總結與提高

本部分內容雖然比較抽象,平時也基本不會用到,但作為背景知識,了解一下沒有壞處。通過熟悉.NET各個版本的更新,我們可以對.NET框架十余年的發展和它所要達到的目標有一個更加明確的認識。.NET的整個發展就是

  1. 不斷統一:例如WCF統一了Web服務曾經有的各種類型的呼叫方式。LINQ統一了各種資源(XML,各類型數據庫)的訪問和篩選方式,如果你熟悉表達式樹,你甚至可以寫一個自己的LINQ TO something。統一的過程就是解放開發者的過程。
  2. 不斷解耦:例如WPF相比Winform,更好的做到了將設計和代碼分開,真正讓兩撥人同時工作。最新的ASP.NET Core徹底和System.Web和IIS解耦。只出現需要的東西,不需要的連影子也不能有。
  3. 提高代碼友好程度:C#中有數不勝數的例子,隨便舉幾個:C# 6的$符號,async和await關鍵字(異步的巔峰),以及那越來越像函數式編程,無處不在的lambda表達式。代碼的可讀性越來越強,甚至完全不懂編程的人,只要他認識英語,就能看懂大概。

幾條主要脈絡:

  1. Web服務:RPC以及其他  -> WCF (SOAP) -> Web API (REST) -> Web API 2 (REST)
  2. Web應用: ASP -> ASP.NET -> ASP.NET MVC -> ASP.NET Core
  3. 數據庫:ADO.NET -> ADO.NET Entity Framework (ORM)
  4. 異步編程:委托 -> 事件 -> 任務 -> 任務語法糖

而未來則是函數式編程的世界,Web App的世界,開源的世界,依賴注入的世界,以及nuget的世界。通過學習.NET的演化史,我認為這個平台的未來是光明的。熟悉.NET的歷史,你可以令人信服的證明你對.NET充滿興趣,在和面試官閑聊時,也是不錯的談資,特別是面試官本人也是技術大牛時,他可能會覺得你是個可造之材。如果你資歷深厚,甚至了解.NET出現之前業界的狀況,那么你對.NET對整個開發產業的改變一定有着比我深入更多的認識,甚至你可以猜測.NET將來的發展方向。

對於程序集這部分,實際上還是有比較多機會接觸到的,了解程序集對后面反射,動態類型和晚期綁定等很多內容的學習大有幫助。

書籍推薦:CLR via C#第一部分


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM