老白在上一篇了解C# 核心概念 – C# 子產品(module)中講解到了module,并且也提及到了我們編譯處理的Program.exe就是一個module。很多同學可能就有疑問了,Program.exe是一個module嗎?不是應該是一個Assembly嗎?我們生成出來的dll不也是一個Assembly嗎?是以,我們就來談談程式集(Assembly),這部分将分成上下兩篇進行呈現。
- 了解C# 核心概念 – C# 程式集(Assembly)上
- 什麼是Assembly
- 如何生成Assembly
- 了解C# 核心概念 – C# 程式集(Assembly)下
- 為什麼需要Assembly
- 示例
- 生成多個module的Assembly
- 添加資源檔案到Assembly
什麼是Assembly
和上一篇的module一樣,Assembly其實也是CLR的概念并非C#獨有。任何一個編譯出來的exe或者dll都是一個Assembly。Assembly具有以下幾個特點:
- 一個Assembly可以包含一個或多個module和資源檔案(resource file)
- 一個Assembly是一個可以單獨并重複使用的最小機關,module不是
- 一個Assembly必然帶有一個清單檔案(manifest)
- 一個Assembly附帶有一個版本号
讓我們來一個個看看這些特點都說明了什麼。這裡老白将使用迅雷斷點續傳的例子來給大家講解,雖然并不完全一緻,或者對應并不準确,但希望通過這種方式能夠友善大家了解。
一個Assembly可以包含一個或多個module和資源檔案(resource file)
這說明了Assembly是module的一個超集。他不僅包含了最重要的主體,也就是各種類型(這些類型都被封裝成了module),還包含了資源檔案。這些資源檔案可以是任意檔案,比如jpg圖檔,或者excel資料表等等。這使得我們可以将一些資料和邏輯代碼隔離開來,而不需要将這部分資訊也變成代碼放進類型中。
其實這個很常見,我們使用迅雷進行大的壓縮檔案下載下傳的時候,可能壓縮檔案除了我們需要下載下傳的(比如視訊),還有另外一個私貨比如廣告檔案或者圖檔。Assembly就類似這個壓縮檔案,視訊就是我們的主體module,而廣告或者圖檔就是我們說到的資源檔案。
一個Assembly是一個可以單獨并重複使用的最小機關,module不是
也就是說,在C#的運作或者使用過程中,我們其實是沒有辦法單獨操作一個module的。我們隻能夠操作或者運作一個Assembly。也就是說,module脫離了Assembly的存在是沒有意義的。我相信很多聰明的同學已經想到了其中一個例子來印證,就是反射。如果大家有印象,我們使用反射的時候用的是Assembly.GetType來獲得某個類型的,從來就沒有使用到module這個概念。
我們在使用迅雷下載下傳的時候也是,斷點續傳就是把一個大的檔案分成了很多小的部分。我們可以認為這些小的部分就是module。當我們單獨拿到一個壓縮檔案的某一小部分的時候,我相信大家都有經驗,這一小部分我們幾乎是無法使用的(之是以說是幾乎,因為有小機率一小部分包含了完整的資訊)。隻有我們把所有這些小部分合在一起組成完整的檔案我們才能正常使用。Assembly和module的關系也就類似于此。
一個Assembly必然帶有一個清單檔案(manifest)
這個可以說是區分一個Assembly和module的一個決定性的标志。一個Assembly必然會有一個manifest檔案。這個檔案中聲明了一些比較重要的資訊,比如這個Assembly中包含了哪些檔案(module或者資源檔案),這個Assembly包含了哪些public的可以被其他Assembly引用的成員等等。
這個清單檔案可以包含在某一個已有的module中,也可以是單獨的不包含任何資訊的新module。
這個manifest就很像我們使用迅雷下載下傳的時候,我們會提前知道我們下載下傳的包含了哪些内容或者檔案。如果沒有這麼一個清單,那麼我們想下載下傳一個馬拉多納停球集錦,但是下載下傳的檔案并不告訴我們他包含了馬拉多納的集錦,那麼我們隻能下載下傳完成後打開看了才知道是什麼内容。這樣就非常沒有效率。是以有了manifest,其他程式就知道這個Assembly有什麼資訊,他們就能夠根據需求決定是否使用,而不需要一一檢測Assembly的内容。
一個Assembly附帶有一個版本号
這裡其實有必要說明下C#經常會用到的幾個版本号(神奇的設定)。
下圖是一個典型的AssemblyInfo.cs的配置,裡面包含了兩個version,一個是AssemblyVersion,還有一個是AssemblyFileVersion,有些時候還可能看到AssemblyInformationVersion。那到底這三個version有什麼關系呢?
- AssemblyFileVersion:這個資訊主要是給人看,CLR并不會檢驗這個版本。這個資訊可以在Assembly的檔案屬性中找到。這個version主要目的是幫助開發人員對使用者系統中的Assembly進行問題排查。
- AssemblyInformationalVersion:這個資訊和AssemblyFileVersion一樣,都主要是給人看的,CLR本身并不會進行校驗。這個資訊也同樣可以在檔案屬性中找到。這個version一個主要目的是聲明這個Assembly是在産品的哪個版本中被使用的。
- AssemblyVersion:這個是我們說到的Assembly的版本号。這個資訊會存儲在manifest中。這個資訊很重要,因為這個版本号是這個Assembly的唯一辨別,當其他Assembly需要進行引用的時候,使用的就是這個AssemblyVersion進行引用。
下面是一個AssemblyInfo.cs的例子:
using System.Reflection;using System.Runtime.CompilerServices;using System.Runtime.InteropServices;[assembly: AssemblyVersion("1.2.3.4")][assembly: AssemblyFileVersion("2.3.4.5")][assembly: AssemblyInformationalVersion("3.4.5.6")]
然後可以使用了解C# 核心概念 – C# 子產品(module) 介紹到的csc來進行編譯打包:
csc.exe Program.cs AssemblyInfo.cs
這裡我們又省略掉了/out參數和/t參數,因為/t的預設參數就是exe,預設也會使用Program的名字來生成exe檔案。
然後我們就可以右鍵檢視生成的Program.exe的屬性,如下:
Program.exe檔案屬性
我們可以看到,之前提及到的AssemblyFileVersion和AssemblyInformationalVersion都在屬性中進行了顯示,但是AssemblyVersion卻沒有。
最後再提及一點這幾個version,他們都是由4個數字和點号組成。其遵守的是[大版本号(major number)].[小版本号(minor number)].[編譯版本号(build number)].[修訂版本号(revision number)]。
如何生成Assembly
我想聰明的同學已經知道了,在上一講了解C# 核心概念 – C# 子產品(module)我們講解到了如何生成module,但是其實我們也得到了一個Assembly,一個隻有一個module的Assembly。
由于直接引入module檔案會讓大家非常難以了解,是以在上一篇中老白模糊了這兩者之間的界線,告訴大家之前生成的Program.exe就是一個module。其實準确的說,Program.exe是一個隻包含了一個module的Assembly。那麼我們是不是有辦法隻生成module而不生成Assembly呢?答案是肯定的,隻需要使用/t:module參數,我們就能夠得到一個.netmodule的檔案。這個檔案就是一個純module,在被組成Assembly之前這個module并不能被使用。
除了這個/t:module參數之外,其他的所有/t參數都會得到一個Assembly。現有的/t參數有:
- exe: 獲得一個控制台應用程式
- winexe:具有UI的應用程式
- library:dll檔案,無法直接運作,隻能被其他應用或者程式引用
- appcontainerexe:隻能在應用容器中運作的應用程式,這種類型生成的程式可以在微軟的應用商店中運作。
- winmdobj:這個類型生成的.winmdobj檔案隻能被WinMDExp.exe進行消費
也就是如下指令,将是我們最常見的生成Assembly的指令:
csc.exe /t:exe [檔案...]csc.exe /t:winexe [檔案...]csc.exe /t:dll [檔案...]
以上的指令都隻是生成一個單module的Assembly。在下一篇中,老白将用示例展示如何生成多個module的Assembly以及包含資源檔案的Assembly。
好了,到此,老白就将什麼是Assembly以及如何生成介紹完了。如果有什麼問題,歡迎大家和老白一起探讨。
碼字不易,如果覺得有用或者喜歡老白的内容,歡迎點贊推薦收藏關注轉發。