C#图解教程 第二十一章 命名空间和程序集
命名空間和程序集
引用其他程序集
在第1章中,我們?cè)诟邔哟紊嫌^察了編譯過程。編譯器接受源代碼文件并生稱名稱為程序集的輸出文件。這一章中,我們將詳細(xì)闡述程序集以及它們是如何生成和部署的。你還會(huì)看到命名空間是如何幫助組織類型的。?
在迄今為止所看到的所有程序中,大部分都聲明并使用它們自己的類。然而,在許多項(xiàng)目中,你會(huì)想使用來(lái)自其他程序集的類或類型。這些其他的程序集可能來(lái)自BCL,或來(lái)自第三方供應(yīng)商,或你自己創(chuàng)建了它們。這些程序集稱為類庫(kù),而且它們的程序集文件的名稱通常以.dll擴(kuò)展名結(jié)尾而不是.exe擴(kuò)展名。?
例如,假設(shè)你想創(chuàng)建一個(gè)類庫(kù),它包含可以被其他程序集使用的類和類型。一個(gè)簡(jiǎn)單庫(kù)的源代碼如下例所示,它包含在名稱為SuperLib.cs文件中。該庫(kù)含有一個(gè)名稱為SquareWidget的公有類。下圖闡明了DLL的生成。
?
要使用Visual Studio創(chuàng)建類庫(kù),在已安裝的Windows模板中創(chuàng)建類庫(kù)模板。具體來(lái)說(shuō),在Visual Studio中進(jìn)行的操作步驟如下。
假設(shè)你還要寫一個(gè)名稱為MyWidgets的程序,而且你想使用SquareWidget類。程序的代碼在一個(gè)名稱為MyWidgets.cs的文件中,如下例所示。這段代碼簡(jiǎn)單創(chuàng)建一個(gè)類型為SquareWidget的對(duì)象并使用該對(duì)象的成員。
using System; class WidgetsProgram {static void Main(){SquareWidget sq = new SquareWidget(); //來(lái)自類庫(kù)↑未在當(dāng)前程序集中聲明sq.SideLength = 5.0; //設(shè)置邊長(zhǎng)Console.WriteLine(sq. Area); //輸出該區(qū)域} ↑ } 未在當(dāng)前程序集中聲明注意,這段代碼沒有聲明類SquareWidget。相反,使用的是定義在SuperLib中的類。然而,當(dāng)你編譯MyWidgets程序時(shí),編譯器必須知道你的代碼在使用程序集SuperLib,這樣它才能得到關(guān)于類SquareWidget的信息。要實(shí)現(xiàn)這點(diǎn),需要給編譯器一個(gè)到該程序集的引用,給出它的名稱和位置。?
在Visual Studio中,可以用下面的方法把引用添加到項(xiàng)目。
- 選擇Solution Explorer,并在該項(xiàng)目名下找到References目錄。References目錄包含項(xiàng)目使用的程序集的列表
- 右鍵單擊References目錄并選擇Add Reference。有5個(gè)可以從中選擇的標(biāo)簽頁(yè),允許你以不同的方法找到類庫(kù)
- 對(duì)于我們的程序,選擇Browse標(biāo)簽,瀏覽到包含SquareWidget類定義的DLL文件,并選擇它
- 點(diǎn)擊OK按鈕,引用就被加入到項(xiàng)目了
在添加了引用之后,可以編譯MyWidgets了。下圖闡明了全部的編譯過程。?
mscorlib庫(kù)
有一個(gè)類庫(kù),我?guī)缀踉谙惹暗拿恳粋€(gè)示例中都使用它。它就是包含Console類的那個(gè)庫(kù)。Console類被定義在名稱為mscorlib的程序集中,在名稱為mscorlib.dll的文件里。然而,你不會(huì)看到這個(gè)程序集被列在References目錄中。程序集mscorlib.dll含有C#類型以及大部分.NET語(yǔ)言的基本類型的定義。在編譯C#程序時(shí),它必須總是被引用,所以Visual Studio不把它顯示在References目錄中。?
如果算上mscorlib,MyWidgets的編譯過程看起來(lái)更像下圖所示的表述。在此之后,我會(huì)假定使用mscorlib程序集而不再描述它。?
?
現(xiàn)在假設(shè)你的程序已經(jīng)很好地用SquareWidget類工作了,但你想擴(kuò)展它的能力以使用一個(gè)名稱為CircleWidget的類,它被定義在另一個(gè)名稱為UltraLib的程序集中。MyWidgets的源代碼看上去像下面這樣。它創(chuàng)建一個(gè)SquareWidget對(duì)象和一個(gè)CircleWidget對(duì)象,它們分別定義在SuperLib中和UltraLib中。
類庫(kù)UltralLib的源代碼如下面的示例所示。注意,除了類CircleWidget之外,就像庫(kù)SuperLib, 它還聲明了一個(gè)名稱為SquareWidget的類 。可以把UltraLib 編譯成一個(gè)DLL并加入到項(xiàng)目MyWidgets的引用列表中。
public class SquareWidget {… } public class CircleWidget {public double Radius =0;public double Area{get {…}} }因?yàn)閮蓚€(gè)庫(kù)都含有名稱為SquareWidget的類,當(dāng)你試圖編譯程序MyWidgets時(shí),編譯器產(chǎn)生一條錯(cuò)誤消息,因?yàn)樗恢朗褂妙怱quareWidget的哪個(gè)版本。下圖闡明了這種命名沖突。?
命名空間
在MyWidgets示例中,由于你有源代碼,你能通過在SuperLib源代碼或UltraLib源代碼中僅僅改變SquareWidget類的名稱來(lái)解決命名沖突。但是,如果這些類是由不同的公司開發(fā)的,而且你還不能擁有源代碼會(huì)怎么樣呢?假設(shè)SuperLib由一個(gè)名稱為MyCorp的公司生產(chǎn),UltraLib由ABCCorp公司生產(chǎn)。在這種情況下,如果你使用了任何有沖突的類或類型,你將不能把這兩個(gè)庫(kù)放在ー起使用。?
你能想象得出,在你做開發(fā)的機(jī)器上含有許多不同公司生產(chǎn)的程序集,很可能有一定數(shù)量的類名重復(fù)。如果僅僅因?yàn)樗鼈兣銮捎泄餐念愋兔?#xff0c;不能把兩個(gè)程序集用在一個(gè)程序中,這將很可惜。?
但是,假設(shè)MyCorp有一個(gè)策略,讓所有類的前綴都是公司名字加上類產(chǎn)品名和描述名。并且進(jìn)一步假設(shè)ABCCorp也有相同的策略。這樣的話,我們示例中的3個(gè)類名就可能是MyCorpSuperLibSquareWidget、ABCCorpUltraLibSquareWidget和ABCCorpUltralLibCircleWidget,如下圖所示。這當(dāng)然是完全有效的類名,并且一個(gè)公司類庫(kù)的類不太可能與其他公司類庫(kù)的類發(fā)生沖突。?
?
但是,在我們的示例程序中,需要使用冗長(zhǎng)的名字,看上去如下所示:
盡管這可以解決沖突問題,但是即使有智能感知,這些新的、已消除歧義的名字還是難以閱讀并且容易出錯(cuò)。?
不過,假設(shè)除了標(biāo)識(shí)符中一般允許的字符,還可以在字符串中使用點(diǎn)--盡管不是在類名的最前面或最后面,那么這些名字就更好理解了,比如MyCorp.SuperLib.SquareWidget、ABCCorp.UltraLib.SquareWidget及ABCCorp.UltraLib.CircleWidget。現(xiàn)在代碼看上去如下所示:
這就給了我們命名空間名和命名空間的定義。
- 你可以把命名空間名視為一個(gè)字符串(在字符串中可以使用點(diǎn)),它加在類名或類型名的前面并且通過點(diǎn)進(jìn)行分隔
- 包括命名空間名、分隔點(diǎn),以及類名的完整字符串叫做類的完全限定名
- 命名空間是共享命名空間名的一組類和類型
你可以使用命名空間來(lái)把一組類型組織在一起并且給它們起一個(gè)名字。一般而言,命名空間名描述的是命名空間中包含的類型,并且和其他命名空間名不同。?
你可以通過在包含你的類型聲明的源文件中聲明命名空間,從而創(chuàng)建命名空間。如下代碼演示了聲明命名空間的語(yǔ)法。然后在命名空間聲明的大括號(hào)中聲明你的所有類和其他類型。那么這些類型就是這個(gè)命名空間的成員了。
如下代碼演示了MyCorp的程序員如何創(chuàng)建MyCorp.SuperLib命名空間以及聲明其中的Squarewidget類。
公司名 點(diǎn)↓ ↓ namespace MyCorp.SuperLib {public class SquareWidget{public double SideLength = 0;public double Area{get { return SideLength * SideLength; }}} }當(dāng)MyCorp公司給你配上更新的程序集時(shí),你可以通過按照如下方式修改MyWidgets程序來(lái)使用它。
class WidgetsProgram {static void Main( ){ 完全限定名 完全限定名↓ ↓MyCorp.SuperLib.SquareWidget sq = new MyCorp.SuperLib.SquareWidget();↑ ↑命名空間名 類名CircleWidget circle = new CircleWidget();…} }既然你在代碼中顯式指定了SquareWidget的SuperLib版本,編譯器不會(huì)再有區(qū)分類的問題了。完全限定名稱輸入起來(lái)有點(diǎn)長(zhǎng),但至少你現(xiàn)在能使用兩個(gè)庫(kù)了。在本章稍后,我會(huì)闡述using別名指令以解決不得不在完全限定名稱中重復(fù)輸入的麻煩。?
如果UltraLib程序集也被生產(chǎn)它的公司(ABCCorp)使用命名空間更新,那么編譯過程會(huì)如下圖所示。?
命名空間名稱
如你所見,命名空間的名稱可以包含創(chuàng)建該程序集的公司的名稱。除了標(biāo)識(shí)公司以外,該名稱還用于幫助程序員快速了解定義在命名空間內(nèi)的類型的種類。?
關(guān)于命名空間名稱的一些要點(diǎn)如下。
- 命名空間名稱可以是任何有效標(biāo)識(shí)符,如第2章所述
- 另外,命名空間名稱可以包括句點(diǎn)符號(hào),用于把類型組織成層次
下表列出了一些在.NET BCL中的命名空間的名稱。?
?
下面是命名空間命名指南:
- 使用公司名開始命名空間名稱
- 在公司名之后跟著技術(shù)名稱
- 不要把命名空間命名為與類或類型相同的名稱
例如,Acme Widget公司的軟件開發(fā)部門在下面3個(gè)命名空間中開發(fā)軟件,如下面的代碼所示:
- AcmeWidgets.SuperWidget
- AcmeWidgets.Media
- AcmeWidgets.Games
命名空間的補(bǔ)充
關(guān)于命名空間,有其他幾個(gè)要點(diǎn)應(yīng)該知道。
- 在命名空間內(nèi),每個(gè)類型名必須有別于所有其他類型
- 命名空間內(nèi)的類型稱為命名空間的成員
- 一個(gè)源文件可以包含任意數(shù)目的命名空間聲明,可以順序也可以嵌套
下圖左邊展示了一個(gè)源文件,它順序聲明了兩個(gè)命名空間,每個(gè)命名空間內(nèi)有幾個(gè)類型。注意,盡管命名空間內(nèi)含有幾個(gè)共有的類名,它們被命名空間名稱區(qū)分開來(lái),如右邊的程序集所示。?
?
.NET框架BCL提供了數(shù)千個(gè)已定義的類和類型以供生成程序時(shí)選擇。為了幫助組織這組有用的功能,相關(guān)功能的類型被聲明在相同的命名空間里。BCL使用超過100個(gè)命名空間來(lái)組織它的類型。
命名空間跨文件伸展
命名空間不是封閉的。這意味著可以在該源文件的后面或另一個(gè)源文件中再次聲明它,以對(duì)它增加更多的類型聲明。?
下面展示了三個(gè)類的聲明,它們都在相同的命名空間中,但聲明在分離的源文件中。源文件可以被編譯成單一的程序集(圖21-9),或編譯成分離的程序集(圖21-10)。?
嵌套命名空間
命名空間可以被嵌套,從而產(chǎn)生嵌套的命名空間。嵌套命名空間允許你創(chuàng)建類型的概念層次。有兩種方法聲明一個(gè)嵌套的命名空間,如下所示。
- 原文嵌套 可以把命名空間的聲明放在一個(gè)封裝的命名空間聲明體內(nèi)部,從而創(chuàng)建一個(gè)嵌套的命名空間。下圖的左邊闡明了這種方法。在這個(gè)示例中,命名空間OtherNs嵌套在命名空間MyNamespace中
- 分離的聲明 也可以為嵌套命名空間創(chuàng)建分離的聲明,但必須在聲明中使用它的完全限定名稱。下圖的右邊闡明了這種方法。注意在嵌套命名空間OtherNs的聲明中,使用全路徑命名MyNamespace.OtherNs。
上圖所示的兩種形式的嵌套命名控件聲明生成相同的程序集。下圖展示了兩個(gè)聲明在SomeLib.cs文件中的類,使用它們的完全限定名。?
雖然嵌套命名空間位于父命名空間內(nèi)部,但是其成員并不屬于包裹的父命名空間。有一個(gè)常見的誤區(qū),認(rèn)為既然嵌套的命名空間位于父命名空間內(nèi)部,其成員也是父命名空間的子集,這是不正確的,命名空間之間是相互獨(dú)立的。
using 指令
完全限定名可能相當(dāng)長(zhǎng),在代碼中通篇使用它們十分繁瑣。然而,有兩個(gè)編譯器指令,可以使你避免使用完全限定名:using命名空間指令和using別名指令。
關(guān)于using指令的兩個(gè)要點(diǎn)如下:
- 它們必須放在源文件的頂端,在任何類型聲明之前
- 它們應(yīng)用于當(dāng)前源文件中的所有命名空間
using命名空間指令
在MyWidgets示例中,你看到多個(gè)部分使用完全限定名稱指定一個(gè)類。可以通過在源文件的頂端放置using命名空間指令以避免不得不使用長(zhǎng)名稱。?
using命名空間指令通知編譯器你將要使用來(lái)自某個(gè)指定命名空間的類型。然后你可以使用簡(jiǎn)單類名而不必全路徑修飾它們。?
當(dāng)編譯器遇到一個(gè)不在當(dāng)前命名空間的名稱時(shí),它檢査在using命名空間指令中給出的命名空間列表,并把該未知名稱加到列表中的第一個(gè)命名空間后面。如果結(jié)果完全限定名稱匹配了這個(gè)程序集或引用程序集中的一個(gè)類,編譯器將使用那個(gè)類。如果不匹配,那么它試驗(yàn)列表中下一個(gè)命名空間。?
using命名空間指令由關(guān)鍵字using跟著一個(gè)命名空間標(biāo)識(shí)符組成。
WriteLine方法,就是類Console的成員,在System命名空間中。不是在通篇代碼中使用它的完全限定名,我只是簡(jiǎn)化了一點(diǎn)我們的工作,在代碼的頂端使用using命名空間指令。?
例如,下面的代碼在第一行使用using命名空間指令以描述該代碼使用來(lái)自System命名空間的類或其他類型。
using別名指令
using別名指令允許起一個(gè)別名給:
- 命名空間
- 命名空間內(nèi)的一個(gè)類型
例如,下面的代碼展示了兩個(gè)using別名指令的使用。第一個(gè)指令告訴編譯器標(biāo)識(shí)符Syst是命名空間System的別名。第二個(gè)指令表明標(biāo)識(shí)符SC是類System.Console的別名。
關(guān)鍵字 別名 命名空間↓ ↓ ↓ using Syst=System; using SC=System.Console;↑類下面的代碼使用這些別名。在Main中3行代碼都調(diào)用System.Console.WriteLine方法。
- Main的第一條語(yǔ)句使用命名空間(System)的別名
- 第二條語(yǔ)句使用該方法的完全限定名
- 第三條語(yǔ)句使用類(Console)的別名
程序集的結(jié)構(gòu)
如第1章所述,程序集不包含本地機(jī)器代碼,而是公共中間語(yǔ)言代碼。它還包含實(shí)時(shí)編譯器(JIT)在運(yùn)行時(shí)轉(zhuǎn)換CIL到本機(jī)代碼所需的一切,包括對(duì)它所引用的其他程序集的引用。?
程序集的文件擴(kuò)展名通常為.exe或.dll。
大部分程序集由一個(gè)單獨(dú)的文件構(gòu)成。下圖闡明了程序集的4個(gè)主要部分。
- 程序集的清單包含以下幾點(diǎn)
- 程序集名稱標(biāo)識(shí)符
- 組成程序集的文件列表
- 一個(gè)指示程序集中內(nèi)容在哪里的地圖
- 關(guān)于引用的其他程序集的信息
- 類型元數(shù)據(jù)部分包含該程序集中定義的所有類型的信息。這些信息包含關(guān)于每個(gè)類型要知道的所有事情
- CIL部分包含程序集的所有中間代碼
- 資源部分是可選的,但可以包含圖形或語(yǔ)言資源
?
程序集代碼文件稱為模塊。盡管大部分程序集由單文件組成,但有些也有多個(gè)文件。對(duì)于有多個(gè)模塊的程序集,一個(gè)文件是主模塊(primary module ),而其他的是次要模塊(secondary modules )。
- 主模塊含有程序集的清單和到次要模塊的引用
- 次要模塊的文件名以擴(kuò)展名.netmodule結(jié)尾
- 多文件程序集被視為一個(gè)單一單元。它們一起部署并一起定版
下圖闡明了一個(gè)帶次要模塊的多文件程序集。?
程序集標(biāo)識(shí)符
在.NET框架中,程序集的文件名不像在其他操作系統(tǒng)和環(huán)境中那么重要,更重要的是程序集的標(biāo)識(shí)符(identity )。?
程序集的標(biāo)識(shí)符有4個(gè)組成部分,它們一起唯一標(biāo)識(shí)了該程序集,如下所示。
- 簡(jiǎn)單名 這只是不帶文件擴(kuò)展名的文件名。每個(gè)程序集都有一個(gè)簡(jiǎn)單名。它也被稱為程序集名或友好名稱(friendly name)
- 版本號(hào) 它由4個(gè)句點(diǎn)分開的整數(shù)字符串組成,形式為MajorVersion.MinorVersion.Build.Revision,例如2.0.35.9
- 文化信息 它是一個(gè)字符串,由2~5個(gè)字符組成,代表一種語(yǔ)言,或代表一種語(yǔ)言和一個(gè)國(guó)家或地區(qū)。例如,在美國(guó)使用英語(yǔ)的文化名是en-US。在中國(guó)使用中文,它是zh-CN。
- 公鑰 這個(gè)128字節(jié)字符串應(yīng)該是生產(chǎn)該程序集的公司唯一的
公鑰是公鑰/私鑰對(duì)的一部分,它們是一組兩個(gè)非常大的、特別選擇的數(shù)字,可以用于創(chuàng)建安全的數(shù)字簽名。公鑰,顧名思義,可以被公開。私鑰必須被擁有者保護(hù)起來(lái)。公鑰是程序集標(biāo)識(shí)符的一部分。我們稍后會(huì)在本章看到私鑰的使用。?
程序集名稱的組成被包含在程序集清單中。下圖闡明了清單部分。?
?
下圖展示了用在.NET文檔和書籍中的關(guān)于程序集標(biāo)識(shí)符的一些術(shù)語(yǔ)。?
強(qiáng)命名程序集
強(qiáng)命名(strongly named)程序集有一個(gè)唯一的數(shù)字簽名依附于它。強(qiáng)命名程序集比沒有強(qiáng)名稱的程序集更加安全,原因有以下幾點(diǎn)。
- 強(qiáng)名稱唯一標(biāo)識(shí)了程序集。沒有其他人能創(chuàng)建一個(gè)與之有相同名稱的程序集,所以用戶可以確信該程序集來(lái)自于其聲稱的來(lái)源
- 沒有CLR安全組件來(lái)捕獲更改,帶強(qiáng)名稱的程序集的內(nèi)容不能被改變
弱命名(weakly named )程序集是沒有被強(qiáng)命名的程序集。由于弱命名程序集沒有數(shù)字簽名, 它天生是不安全的。因?yàn)橐桓湹膹?qiáng)度只和它最弱的一環(huán)相同,所以強(qiáng)命名程序集默認(rèn)只能訪問其他強(qiáng)命名程序集(還存在一種方法允許“部分地相信調(diào)用者”,但本書不做闡述)。?
程序員不產(chǎn)生強(qiáng)名稱。編譯器產(chǎn)生它,接受關(guān)于程序集的信息,并散列化這些信息以創(chuàng)建一個(gè)唯一的數(shù)據(jù)簽名依附到該程序集。它在散列處理中使用的信息如下:
- 組成程序集的字節(jié)序列
- 簡(jiǎn)單名稱
- 版本號(hào)
- 文化信息
- 公鑰/私鑰對(duì)
在圍繞強(qiáng)名稱的命名法方面有一些差異。本書所指的“強(qiáng)命名的”常指的是“強(qiáng)名稱的”。 “弱命名的”有時(shí)指的是“非強(qiáng)命名的”或“帶簡(jiǎn)單名稱的程序集”。
創(chuàng)建強(qiáng)命名程序集
要使用Visual Studio強(qiáng)命名一個(gè)程序集,必須有一份公鑰/私鑰對(duì)文件的副本。如果沒有密鑰文件,可以讓Visual Studio產(chǎn)生一個(gè)。可以實(shí)行以下步驟。
在編譯代碼時(shí),編譯器會(huì)生成一個(gè)強(qiáng)命名的程序集。編譯器的輸入和輸出如下圖?
要?jiǎng)?chuàng)建強(qiáng)命名程序集還可以使用Strong Name工具(sn.exe ),這個(gè)工具在安裝Visual Studio 的時(shí)候會(huì)自動(dòng)安裝。它是個(gè)命令行工具,允許程序員為程序集簽名,還能提供大量管理密鑰和簽名的其他選項(xiàng)。如果Visual Studio IDE還不符合你的要求,它能提供更多選擇。要使用Strong Name工具,可到網(wǎng)上查閱更多細(xì)節(jié)。
程序集的私有方式部署
在目標(biāo)機(jī)器上部署一個(gè)程序就像在該機(jī)器上創(chuàng)建一個(gè)目錄并把應(yīng)用程序復(fù)制過去一樣簡(jiǎn)單。如果應(yīng)用程序不需要其他程序集(比如DLL),或如果所需的DLL在同一目錄下,那么程序應(yīng)該會(huì)就在它所在的地方良好工作。這種方法部署的程序集稱為私有程序集,而且這種部署方法稱為復(fù)制文件(XCopy)部署。?
私有程序集幾乎可以被放在任何目錄中,而且只要它們依賴的文件都在同一目錄或子目錄下就足夠了。事實(shí)上,可以在文件系統(tǒng)的不同部分有多個(gè)目錄,每個(gè)目錄都有同樣的一組程序集,并且它們都會(huì)在它們各自不同的位置良好工作。?
關(guān)于私有程序集部署的一些重要事情如下:
- 私有程序集所在的目錄被稱為應(yīng)用程序目錄
- 私有程序集可以是強(qiáng)命名的也可以是弱命名的
- 沒有必要在注冊(cè)表中注冊(cè)組件
- 要卸載一個(gè)私有程序集,只要從文件系統(tǒng)中刪除它即可
共享程序集和GAC
私有程序集是非常有用的,但有時(shí)你會(huì)想把一個(gè)DLL放在一個(gè)中心位置,這樣一個(gè)單獨(dú)的復(fù)制就能被系統(tǒng)中其他的程序集共享。.NET有這樣的貯藏庫(kù),稱為全局程序集緩存(GAC)。放進(jìn)GAC的程序集稱為共享程序集。?
關(guān)于GAC的一些重要內(nèi)容如下:
- 只有強(qiáng)命名程序集能被添加到GAC
- GAC的早期版本只接受帶.dll擴(kuò)展名的文件,現(xiàn)在也可以添加帶.exe擴(kuò)展名的程序集了
- GAC位于Windows系統(tǒng)目錄的子目錄中。.NET4.0之前位于\Windows\Assembly中,從.NET4.0開始位于\Windows\Microsoft.NET\assembly中
把程序集安裝到GAC
當(dāng)試圖安裝一個(gè)程序集到GAC時(shí),CLR的安全組件首先必須檢驗(yàn)程序集上的數(shù)字簽名是否有效。如果沒有數(shù)據(jù)簽名,或它是無(wú)效的,系統(tǒng)將不會(huì)把它安裝到GAC。?
然而,這是個(gè)一次性檢査。在程序集已經(jīng)在GAC內(nèi)之后,當(dāng)它被一個(gè)正在運(yùn)行的程序引用時(shí),不再需要進(jìn)一步的檢査。?
gacutil.exe命令行工具允許從GAC添加或刪除程序集,并列出GAC包含的程序集。它的3個(gè)最有用的參數(shù)標(biāo)記如下所示。
- /i: 把一個(gè)程序集插人GAC
- /u: 從GAC卸載一個(gè)程序集
- /l: 列出GAC中的程序集
GAC內(nèi)的并肩執(zhí)行
在程序集部署到GAC之后,它就能被系統(tǒng)中其他程序集使用了。然而,請(qǐng)記住程序集的標(biāo)識(shí)符由完全限定名稱的全部4個(gè)部分組成。所以,如果一個(gè)庫(kù)的版本號(hào)改變了,或如果它有一個(gè)不同的公鑰,這些區(qū)別指定了不同的程序集。?
結(jié)果就是在GAC中可以有許多不同的程序集,它們有相同的文件名。雖然它們有相同的文件名,但它們是不同的程序集而且在GAC中完美地共存。這使不同的應(yīng)用程序在同一時(shí)間很容易使用不同版本的同一DLL,因?yàn)樗鼈兪菐Р煌瑯?biāo)識(shí)符的不同程序集。這被稱為并肩執(zhí)行(side-by-side Execution )。?
下圖闡明了GAC中4個(gè)不同的DLL,它們都有相同的文件名 MyLibary.dll。圖中,可以看出前3個(gè)來(lái)自于同一公司,因?yàn)樗鼈冇邢嗤墓€,第4個(gè)來(lái)源不同,因?yàn)樗幸粋€(gè)不同的公鑰。這些版本如下:
- 英文V1.0.0.0版,來(lái)白A公司
- 英文V2.0.0.0版,來(lái)自A公司
- 德文V1.0.0.0版,來(lái)自A公司
- 英文V1.0.0.0版,來(lái)自B公司
配置文件
配置文件含有關(guān)于應(yīng)用程序的信息,供CLR在運(yùn)行時(shí)使用。它們可以指示CLR去做這樣的事情,比如使用一個(gè)不同版本的DLL,或搜索程序引用的DLL時(shí)在附加目錄中查找。?
配置文件由XML代碼組成,并不包含C#代碼。編寫XML代碼的細(xì)節(jié)超出了本書的范圍,但應(yīng)當(dāng)理解配置文件的目的以及它們?nèi)绾问褂谩K鼈兊囊环N用途是更新一個(gè)應(yīng)用程序集以使用新版本的DLL。?
例如,假設(shè)有一個(gè)應(yīng)用程序引用了GAC中的一個(gè)DLL。在應(yīng)用程序的清單中,該引用的標(biāo)識(shí)符必須完全匹配GAC中程序集的標(biāo)識(shí)符。如果一個(gè)新版本的DLL發(fā)布了,它可以被添加到GAC中,在那里它可以幸福地和老版本共存。?
然而,應(yīng)用程序仍然在它的清單中包括老版本DLL的標(biāo)識(shí)符。除非重新編譯應(yīng)用程序并使它引用新版本的DLL,否則它會(huì)繼續(xù)使用老版本。如果這是你想要的,那也不錯(cuò)。?
然而,如果你不想重新編譯程序但又希望它使用新的DLL,那么你可以創(chuàng)建一個(gè)配置文件告訴CLR去使用新的版本而不是舊版本。配置文件被放在應(yīng)用程序目錄中。?
下圖闡明了運(yùn)行時(shí)過程中的對(duì)象。左邊的應(yīng)用程序MyProgram.exe調(diào)用MyLibrary.dll的1.0.0.0版,如點(diǎn)化線箭頭所示。但應(yīng)用程序有一個(gè)配置文件,而它指示CLR加載2.0.0.0版。注意配置文件的名稱由執(zhí)行文件的全名(包括擴(kuò)展名)加上附加擴(kuò)展名.config組成。?
延遲簽名
公司小心地保護(hù)它們官方的公鑰/私鑰對(duì)是非常重要的,否則,如果不可靠的人得到了它,就可以發(fā)布偽裝成該公司的代碼。為了避免這種情況,公司顯然不能允許自由訪問含有它們的公鑰/私鑰對(duì)的文件。在大公司中,最終程序集的強(qiáng)命名經(jīng)常在開發(fā)過程的最尾部由特殊的有密鑰訪問權(quán)限的小組執(zhí)行。?
可是,由于個(gè)別原因,這會(huì)在開發(fā)和測(cè)試過程中導(dǎo)致問題。首先,由于公鑰是程序集標(biāo)識(shí)符的4個(gè)部分之一,所以直到提供了公鑰它才能被設(shè)置。而且,弱命名的程序集不能被部署到GAC。開發(fā)人員和測(cè)試人員都需要有能力編譯和測(cè)試該代碼,并使用它將要被部署發(fā)布的方式,包括它的標(biāo)識(shí)符和在GAC中的位置。?
為了允許這個(gè),有一種修改了的賦值強(qiáng)命名的形式,稱為延遲簽名(delayed signing)或部分簽名(partial signing),它克服了這些問題,而且沒有釋放對(duì)私鑰的訪問。?
在延遲簽名中,編譯器只使用公鑰/私鑰對(duì)中的公鑰。然后公鑰可以被放在完成的程序集的標(biāo)識(shí)符清單中。延遲簽名還使用一個(gè)為0的塊保留數(shù)字簽名的位賈。?
要?jiǎng)?chuàng)建一個(gè)延遲簽名的程序集,必須做兩件事情。第一,創(chuàng)建一個(gè)密鑰文件的副本,它只有公鑰而不是公鑰/私鑰對(duì)。下一步,為程序集范圍內(nèi)的源代碼添加一個(gè)名稱為 DelaySignAttribute 的附加特性,并把它的值設(shè)為true。
下圖展示了生成一個(gè)延遲簽名程序集的輸人和輸出。注意圖中下面的內(nèi)容。
- 在輸人中,DelaySignAttribute 定位于源文件中,而且密鑰文件只含有公鑰
- 在輸出中,在程序集的底部有一個(gè)數(shù)字簽名的保留空間
如果你試圖部署延遲簽名的程序集到GAC,CLR不會(huì)允許,因?yàn)樗皇菑?qiáng)命名的。要在這臺(tái)機(jī)器上部署它,必須首先使用命令行指令取消在這臺(tái)機(jī)器上的GAC簽名確認(rèn),只針對(duì)這個(gè)程序集,并允許它被裝在GAC中。要做到這點(diǎn),從Visual Studio命令提示中執(zhí)行下面的命令。?
sn -vr MyAssembly.dll?
現(xiàn)在,你已經(jīng)看到弱命名程序集、延遲簽名程序集和強(qiáng)簽名程序集。下圖總結(jié)了它們的結(jié)構(gòu)區(qū)別。?
from:?http://www.cnblogs.com/moonache/p/7302375.html
轉(zhuǎn)載于:https://www.cnblogs.com/GarfieldEr007/p/10126611.html
總結(jié)
以上是生活随笔為你收集整理的C#图解教程 第二十一章 命名空间和程序集的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【数据结构 JavaScript版】-
- 下一篇: 冰与火之歌:「时间」与「空间」复杂度