Program.exe並不是只是含有元數據的PE文件,它仍是程序集。程序集是一個或多個類型定義文件及資源文件的集合。在程序集的全部文件中,有一個文件容納了清單(manifest)。清單也是一個元數據表集合。表中主要包含做爲程序集組成部分的那些文件名稱。此外,還描述了程序集的版本、語言文化、發佈者、公開導出的類型以及構成程序集的全部文件。編程
CLR操做的是程序集。也就是說,CLR老是首先加載包含「清單」元數據表的文件,再根據「清單」獲取程序集中的其餘文件的沒名稱。程序集的特色以下:安全
●定義了可重用的類型編程語言
●用一個版本號標記函數
●能夠關聯安全信息工具
這些特色是包含清單的元數據表的文件才具有的。spa
使用程序集,可重用類型的邏輯表示和物理表示就能夠分開。例如,程序集可能包含多個類型,能夠將經常使用類型放到一個文件中,不經常使用類型放到另外一個文件中。若是程序集要從Internet下載而且部署,那麼對於含有不經常使用類型的文件,加入客戶端永遠不適用那些類型,該文件就永遠不會下載到客戶端。.net
總結一下使用多文件程序集的理由:命令行
●不一樣的類型用不一樣的文件,使文件以「增量」下載。另外,將類型劃分到不一樣文件中,能夠對購買和安裝的應用程序部分或分批打包、部署。code
●可在程序集中添加資源或數據文件。例如,假定一個類型的做用是計算保險信息,須要訪問精度表才能完成計算。在這種狀況下,沒必要在本身的源碼中嵌入精度表。相反,可使用一個工具,使數據文件成爲程序集的一部分。BTW,數據文件能夠是任意格式,只要程序知道如何解析。blog
●程序集包含的各個類型能夠用不一樣的編程語言來實現。編譯不一樣語言類型的時候會生成不一樣的模塊,而後能夠用工具將全部模塊合併成單個程序集。
生成程序集要麼選擇現有的PE文件做爲「清單」的宿主,要麼建立單獨的PE文件並只在其中包含清單。因爲有了清單的存在,程序集的用戶沒必要關心程序集的劃分細節。此外,清單也使程序集具備了自描述性。另外,在包含清單的文件中,一些元數據信息描述了哪些文件是程序集的一部分。可是,那些文件自己並不包含元數據來指出他們是程序集的一部分。
能夠經過不少方式將模塊添加到程序集。這裏使用/addmodule開關。如今有兩個源代碼:
Program.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 8 namespace Project_1 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 System.Console.WriteLine("HaHa"); 15 Class1 C1 = new Class1(); 16 C1.printHaha(); 17 } 18 } 19 }
Class1.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Project_1 8 { 9 public class Class1 10 { 11 public void printHaha (){ 12 System.Console.WriteLine("Class1 Haha"); 13 } 14 } 15 }
先將不經常使用的類型都編譯到一個模塊:
此時生成一個Class1.netmodule文件。這是一個PE文件,可是不能被CLR加載。
接着將Program.cs編譯到另外一個模塊。該模塊將成爲程序集清單的宿主。將該模塊命名爲MultiFileLibrary.dll:
上述命令行指示C#編譯器編譯Program.cs來生成MultiFileLibrary.dll。因爲制定了/t:library開關,因此生成的是含有清單元數據表的DLL PE文件。/addmodule:Class1.netmodule 告訴編譯器將文件添加到FIleD二分清單源數據表,並將Class1.netmodule的公開導出類型添加到ExportedTypes Def清單元數據表。
Class1.netmodule文件包含編譯Class1.cs所生成的IL代碼。該文件還包含一些定義源數據表,描述了Class1.cs定義的類型、方法、字段、屬性、事件等。還包含一些引用元數據表,描述了Class1.cs引用的類型、方法等。
MultiFileLibrary.dll是一個單獨的文件。和Class1.netmodule類似,MultiFileLibrary.dll包含編譯Program.cs所生成的IL代碼以及相似的定義與引用元數據表。此外,MultiFileLibrary.dll還包含額外的清單元數據表,這使得MultiFileLibrary.dll成爲程序集。清單元數據表描述了程序集的全部文件(MultiFileLibrary.dll自己和Class1.netmodule)。清單元數據表還包含從Class1.netmodule和MultiFileLibrary.dll導出的全部公共類型。
可用ILDasm檢查元數據清單表(摘取部分):
File #1 (26000001) ------------------------------------------------------- Token: 0x26000001 Name : Class1.netmodule HashValue Blob : 06 f0 35 e1 e5 70 9e a4 6c 9d 4c dc 6b 40 6f c0 9e b5 09 56 Flags : [ContainsMetaData] (00000000) ExportedType #1 (27000001) ------------------------------------------------------- Token: 0x27000001 Name: Project_1.Class1 Implementation token: 0x26000001 TypeDef token: 0x02000002 Flags : [Public] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit] (00100001) User Strings -------------------------------------------------------
從中能夠看出,Class1.netmodule文件已經被當作是程序集的一部分。token是0x26000001。在ExportedType 能夠看到一個公開導出的類型(只有public會顯示在這個數據表中)
客戶端執行代碼時會調用方法。一個方法首次被調用時,CLR檢測做爲參數、返回值或者局部變量而被方法引用的類型。而後,CLR嘗試加載所引用程序及中含有的清單文件。若是要訪問的類型剛好出如今這個文件中,CLR會執行內部登記工做,容許使用該類型。若是清淡指出被引用的類型在不一樣文件中,CLR會嘗試加載須要的文件,一樣執行內部登記,並容許使用該類型。只有在調用方法確實引用了未加載程序集中的類型時,纔會加載程序集。
使用VS IDE將程序集添加到項目中
這個路徑要與生成MultiFileLibrary.dll的路徑一致。
使用程序集連接器
除了使用C#編譯器,還可使用「程序集連接器」實用程序AL.exe來建立程序集。若是程序集要包含由不一樣編譯器生成的模塊,或者生成時不清楚程序集的打包要求,程序集連接器就顯得至關有用。還可使用AL.exe生成只含資源的程序集,也就是附屬程序集,一般用於本地化。
首先,有三個源代碼:
Program.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Project_1 8 { 9 public class Program 10 { 11 public static void Main(string[] args) 12 { 13 System.Console.WriteLine("HaHa"); 14 Class1 C1 = new Class1(); 15 C1.printHaha(); 16 Class2 C2 = new Class2(); 17 C2.printHaha(); 18 } 19 } 20 }
Class1.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Project_1 8 { 9 public class Class1 10 { 11 public void printHaha (){ 12 System.Console.WriteLine("Class1 Haha"); 13 } 14 } 15 }
Class2.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Project_1 { public class Class2 { public void printHaha() { System.Console.WriteLine("Class2 Haha"); } } }
爲了理解AL.exe的工做原理,先改變MultiFileLibrary.dll的生成方式:
生成MultiFileLibrary.dll:
指定入口並獲得EXE PE文件:
(這裏在操做的時候,出現了一個問題:指定main函數的時候報錯「找不到Program.main」,後來才發現緣由有兩個:第一個是沒有加命名空間Project_1,以及代碼中main函數沒有寫錯public,固然如今都加上了。須要指定做用域是由於程序集有可能有多個做用域,因此要指定是哪一個做用域下的。可是public這個到如今都沒有搞清楚具體緣由,不知道有沒有懂的大佬解釋一下,我只知道和.exe的執行過程有關,可是main是在哪裏發生了「外部調用」這個具體的過程還有些模糊)
能夠執行Program.exe:
爲程序集添加資源文件:
AL.exe建立程序集時,能夠用/embed將文件做爲資源添加到程序集。該開關獲取任意文件,並將文件內容嵌入最終的PE文件。清單中的ManifestResourceDef表會更新以反映資源的存在。
與AL.exe類似,C#編譯器CSC.exe也容許將資源合併到編譯器生成的程序集中。/resource開關將指定的資源文件嵌入最終生成的程序集PE中,並更新ManifestResourceDef表。/linkresource開關ManifestResourceDef和FileDef清單表中添加記錄項來引用獨立存在的資源文件。