命名空間和程序集
引用其他程序集
在第1章中,我們在高層次上觀察了編譯過程。編譯器接受源代碼文件並生稱名稱為程序集的輸出文件。這一章中,我們將詳細闡述程序集以及它們是如何生成和部署的。你還會看到命名空間是如何幫助組織類型的。
在迄今為止所看到的所有程序中,大部分都聲明並使用它們自己的類。然而,在許多項目中,你會想使用來自其他程序集的類或類型。這些其他的程序集可能來自BCL,或來自第三方供應商,或你自己創建了它們。這些程序集稱為類庫,而且它們的程序集文件的名稱通常以.dll擴展名結尾而不是.exe擴展名。
例如,假設你想創建一個類庫,它包含可以被其他程序集使用的類和類型。一個簡單庫的源代碼如下例所示,它包含在名稱為SuperLib.cs文件中。該庫含有一個名稱為SquareWidget的公有類。下圖闡明了DLL的生成。
public class SquareWidget
{
public double SideLength=0;
public double Area
{
get{return SideLength*SideLength;}
}
}
要使用Visual Studio創建類庫,在已安裝的Windows模板中創建類庫模板。具體來說,在Visual Studio中進行的操作步驟如下。
- 選擇File→New→Project,打開New Project窗口
- 左邊的面板,在Installed→Templates面板中找到Visual C#節點並選中
- 在中間的面板中選擇Class Library模板
假設你還要寫一個名稱為MyWidgets的程序,而且你想使用SquareWidget類。程序的代碼在一個名稱為MyWidgets.cs的文件中,如下例所示。這段代碼簡單創建一個類型為SquareWidget的對象並使用該對象的成員。
using System;
class WidgetsProgram
{
static void Main()
{
SquareWidget sq = new SquareWidget(); //來自類庫
↑
未在當前程序集中聲明
sq.SideLength = 5.0; //設置邊長
Console.WriteLine(sq. Area); //輸出該區域
} ↑
} 未在當前程序集中聲明
注意,這段代碼沒有聲明類SquareWidget。相反,使用的是定義在SuperLib中的類。然而,當你編譯MyWidgets程序時,編譯器必須知道你的代碼在使用程序集SuperLib,這樣它才能得到關於類SquareWidget的信息。要實現這點,需要給編譯器一個到該程序集的引用,給出它的名稱和位置。
在Visual Studio中,可以用下面的方法把引用添加到項目。
- 選擇Solution Explorer,並在該項目名下找到References目錄。References目錄包含項目使用的程序集的列表
- 右鍵單擊References目錄並選擇Add Reference。有5個可以從中選擇的標簽頁,允許你以不同的方法找到類庫
- 對於我們的程序,選擇Browse標簽,瀏覽到包含SquareWidget類定義的DLL文件,並選擇它
- 點擊OK按鈕,引用就被加入到項目了
在添加了引用之后,可以編譯MyWidgets了。下圖闡明了全部的編譯過程。
mscorlib庫
有一個類庫,我幾乎在先前的每一個示例中都使用它。它就是包含Console類的那個庫。Console類被定義在名稱為mscorlib的程序集中,在名稱為mscorlib.dll的文件里。然而,你不會看到這個程序集被列在References目錄中。程序集mscorlib.dll含有C#類型以及大部分.NET語言的基本類型的定義。在編譯C#程序時,它必須總是被引用,所以Visual Studio不把它顯示在References目錄中。
如果算上mscorlib,MyWidgets的編譯過程看起來更像下圖所示的表述。在此之后,我會假定使用mscorlib程序集而不再描述它。
現在假設你的程序已經很好地用SquareWidget類工作了,但你想擴展它的能力以使用一個名稱為CircleWidget的類,它被定義在另一個名稱為UltraLib的程序集中。MyWidgets的源代碼看上去像下面這樣。它創建一個SquareWidget對象和一個CircleWidget對象,它們分別定義在SuperLib中和UltraLib中。
class WidgetsProgram
{
static void Main()
{
SquareWidget sq = new SquareWidget (); //來自 SuperLib
…
CircleWidget circle = new CircleWidget(); //來自 UltraLib
…
}
}
類庫UltralLib的源代碼如下面的示例所示。注意,除了類CircleWidget之外,就像庫SuperLib, 它還聲明了一個名稱為SquareWidget的類 。可以把UltraLib 編譯成一個DLL並加入到項目MyWidgets的引用列表中。
public class SquareWidget
{
…
}
public class CircleWidget
{
public double Radius =0;
public double Area
{
get {…}
}
}
因為兩個庫都含有名稱為SquareWidget的類,當你試圖編譯程序MyWidgets時,編譯器產生一條錯誤消息,因為它不知道使用類SquareWidget的哪個版本。下圖闡明了這種命名沖突。
命名空間
在MyWidgets示例中,由於你有源代碼,你能通過在SuperLib源代碼或UltraLib源代碼中僅僅改變SquareWidget類的名稱來解決命名沖突。但是,如果這些類是由不同的公司開發的,而且你還不能擁有源代碼會怎么樣呢?假設SuperLib由一個名稱為MyCorp的公司生產,UltraLib由ABCCorp公司生產。在這種情況下,如果你使用了任何有沖突的類或類型,你將不能把這兩個庫放在ー起使用。
你能想象得出,在你做開發的機器上含有許多不同公司生產的程序集,很可能有一定數量的類名重復。如果僅僅因為它們碰巧有共同的類型名,不能把兩個程序集用在一個程序中,這將很可惜。
但是,假設MyCorp有一個策略,讓所有類的前綴都是公司名字加上類產品名和描述名。並且進一步假設ABCCorp也有相同的策略。這樣的話,我們示例中的3個類名就可能是MyCorpSuperLibSquareWidget、ABCCorpUltraLibSquareWidget和ABCCorpUltralLibCircleWidget,如下圖所示。這當然是完全有效的類名,並且一個公司類庫的類不太可能與其他公司類庫的類發生沖突。
但是,在我們的示例程序中,需要使用冗長的名字,看上去如下所示:
class WidgetsProgram
{
static void Main()
{
MyCorpSuperLibSquareWidget sq
=new MyCorpSuperLibSquareWidget(); //來自SuperLib
…
ABCCorpUltraLibCircleWidget circle
=new ABCCorpUltraLibCircleWidget(); //來自UltraLib
…
}
}
盡管這可以解決沖突問題,但是即使有智能感知,這些新的、已消除歧義的名字還是難以閱讀並且容易出錯。
不過,假設除了標識符中一般允許的字符,還可以在字符串中使用點--盡管不是在類名的最前面或最后面,那么這些名字就更好理解了,比如MyCorp.SuperLib.SquareWidget、ABCCorp.UltraLib.SquareWidget及ABCCorp.UltraLib.CircleWidget。現在代碼看上去如下所示:
class WidgetsProgram
{
static void Main()
{
MyCorp.SuperLib.SquareWidget sq
=new MyCorp.SuperLib.SquareWidget(); //來自SuperLib
…
ABCCorp.UltraLib.CircleWidget circle
=new ABCCorp.UltraLib.CircleWidget(); //來自UltraLib
…
}
}
這就給了我們命名空間名和命名空間的定義。
- 你可以把命名空間名視為一個字符串(在字符串中可以使用點),它加在類名或類型名的前面並且通過點進行分隔
- 包括命名空間名、分隔點,以及類名的完整字符串叫做類的完全限定名
- 命名空間是共享命名空間名的一組類和類型
你可以使用命名空間來把一組類型組織在一起並且給它們起一個名字。一般而言,命名空間名描述的是命名空間中包含的類型,並且和其他命名空間名不同。
你可以通過在包含你的類型聲明的源文件中聲明命名空間,從而創建命名空間。如下代碼演示了聲明命名空間的語法。然后在命名空間聲明的大括號中聲明你的所有類和其他類型。那么這些類型就是這個命名空間的成員了。
關鍵字 命名空間名
↓ ↓
namespace NamespaceName
{
TypeDeclarations
}
如下代碼演示了MyCorp的程序員如何創建MyCorp.SuperLib命名空間以及聲明其中的Squarewidget類。
公司名 點
↓ ↓
namespace MyCorp.SuperLib
{
public class SquareWidget
{
public double SideLength = 0;
public double Area
{
get { return SideLength * SideLength; }
}
}
}
當MyCorp公司給你配上更新的程序集時,你可以通過按照如下方式修改MyWidgets程序來使用它。
class WidgetsProgram
{
static void Main( )
{ 完全限定名 完全限定名
↓ ↓
MyCorp.SuperLib.SquareWidget sq = new MyCorp.SuperLib.SquareWidget();
↑ ↑
命名空間名 類名
CircleWidget circle = new CircleWidget();
…
}
}
既然你在代碼中顯式指定了SquareWidget的SuperLib版本,編譯器不會再有區分類的問題了。完全限定名稱輸入起來有點長,但至少你現在能使用兩個庫了。在本章稍后,我會闡述using別名指令以解決不得不在完全限定名稱中重復輸入的麻煩。
如果UltraLib程序集也被生產它的公司(ABCCorp)使用命名空間更新,那么編譯過程會如下圖所示。
命名空間名稱
如你所見,命名空間的名稱可以包含創建該程序集的公司的名稱。除了標識公司以外,該名稱還用於幫助程序員快速了解定義在命名空間內的類型的種類。
關於命名空間名稱的一些要點如下。
- 命名空間名稱可以是任何有效標識符,如第2章所述
- 另外,命名空間名稱可以包括句點符號,用於把類型組織成層次
下表列出了一些在.NET BCL中的命名空間的名稱。
下面是命名空間命名指南:
- 使用公司名開始命名空間名稱
- 在公司名之后跟着技術名稱
- 不要把命名空間命名為與類或類型相同的名稱
例如,Acme Widget公司的軟件開發部門在下面3個命名空間中開發軟件,如下面的代碼所示:
- AcmeWidgets.SuperWidget
- AcmeWidgets.Media
- AcmeWidgets.Games
namespace AcmeWidgets.SuperWidget
{
class SPDBase …
…
}
命名空間的補充
關於命名空間,有其他幾個要點應該知道。
- 在命名空間內,每個類型名必須有別於所有其他類型
- 命名空間內的類型稱為命名空間的成員
- 一個源文件可以包含任意數目的命名空間聲明,可以順序也可以嵌套
下圖左邊展示了一個源文件,它順序聲明了兩個命名空間,每個命名空間內有幾個類型。注意,盡管命名空間內含有幾個共有的類名,它們被命名空間名稱區分開來,如右邊的程序集所示。
.NET框架BCL提供了數千個已定義的類和類型以供生成程序時選擇。為了幫助組織這組有用的功能,相關功能的類型被聲明在相同的命名空間里。BCL使用超過100個命名空間來組織它的類型。
命名空間跨文件伸展
命名空間不是封閉的。這意味着可以在該源文件的后面或另一個源文件中再次聲明它,以對它增加更多的類型聲明。
下面展示了三個類的聲明,它們都在相同的命名空間中,但聲明在分離的源文件中。源文件可以被編譯成單一的程序集(圖21-9),或編譯成分離的程序集(圖21-10)。
嵌套命名空間
命名空間可以被嵌套,從而產生嵌套的命名空間。嵌套命名空間允許你創建類型的概念層次。有兩種方法聲明一個嵌套的命名空間,如下所示。
- 原文嵌套 可以把命名空間的聲明放在一個封裝的命名空間聲明體內部,從而創建一個嵌套的命名空間。下圖的左邊闡明了這種方法。在這個示例中,命名空間OtherNs嵌套在命名空間MyNamespace中
- 分離的聲明 也可以為嵌套命名空間創建分離的聲明,但必須在聲明中使用它的完全限定名稱。下圖的右邊闡明了這種方法。注意在嵌套命名空間OtherNs的聲明中,使用全路徑命名MyNamespace.OtherNs。
上圖所示的兩種形式的嵌套命名控件聲明生成相同的程序集。下圖展示了兩個聲明在SomeLib.cs文件中的類,使用它們的完全限定名。
雖然嵌套命名空間位於父命名空間內部,但是其成員並不屬於包裹的父命名空間。有一個常見的誤區,認為既然嵌套的命名空間位於父命名空間內部,其成員也是父命名空間的子集,這是不正確的,命名空間之間是相互獨立的。
using 指令
完全限定名可能相當長,在代碼中通篇使用它們十分繁瑣。然而,有兩個編譯器指令,可以使你避免使用完全限定名:using命名空間指令和using別名指令。
關於using指令的兩個要點如下:
- 它們必須放在源文件的頂端,在任何類型聲明之前
- 它們應用於當前源文件中的所有命名空間
using命名空間指令
在MyWidgets示例中,你看到多個部分使用完全限定名稱指定一個類。可以通過在源文件的頂端放置using命名空間指令以避免不得不使用長名稱。
using命名空間指令通知編譯器你將要使用來自某個指定命名空間的類型。然后你可以使用簡單類名而不必全路徑修飾它們。
當編譯器遇到一個不在當前命名空間的名稱時,它檢査在using命名空間指令中給出的命名空間列表,並把該未知名稱加到列表中的第一個命名空間后面。如果結果完全限定名稱匹配了這個程序集或引用程序集中的一個類,編譯器將使用那個類。如果不匹配,那么它試驗列表中下一個命名空間。
using命名空間指令由關鍵字using跟着一個命名空間標識符組成。
關鍵字
↓
using System;
↑
命名空間的名稱
WriteLine方法,就是類Console的成員,在System命名空間中。不是在通篇代碼中使用它的完全限定名,我只是簡化了一點我們的工作,在代碼的頂端使用using命名空間指令。
例如,下面的代碼在第一行使用using命名空間指令以描述該代碼使用來自System命名空間的類或其他類型。
using System;
…
System.Console.WriteLine("This is text 1");//使用完全限定名
COnsole.WriteLine("This is text 2); //使用指令
using別名指令
using別名指令允許起一個別名給:
- 命名空間
- 命名空間內的一個類型
例如,下面的代碼展示了兩個using別名指令的使用。第一個指令告訴編譯器標識符Syst是命名空間System的別名。第二個指令表明標識符SC是類System.Console的別名。
關鍵字 別名 命名空間
↓ ↓ ↓
using Syst=System;
using SC=System.Console;
↑
類
下面的代碼使用這些別名。在Main中3行代碼都調用System.Console.WriteLine方法。
- Main的第一條語句使用命名空間(System)的別名
- 第二條語句使用該方法的完全限定名
- 第三條語句使用類(Console)的別名
using Syst = System; // using 命名空間別名指令
using SC = System.Console; // using 類別名指令
namespace MyNamespace
{
class SomeClass
{
static void Main()
{
Syst.Console.WriteLine("Using the namespace alias.");
System.Console.WriteLine("Using fully qualified name.");
SC.WriteLine("Using the type alias");
}類的別名
}
}
}
程序集的結構
如第1章所述,程序集不包含本地機器代碼,而是公共中間語言代碼。它還包含實時編譯器(JIT)在運行時轉換CIL到本機代碼所需的一切,包括對它所引用的其他程序集的引用。
程序集的文件擴展名通常為.exe或.dll。
大部分程序集由一個單獨的文件構成。下圖闡明了程序集的4個主要部分。
- 程序集的清單包含以下幾點
- 程序集名稱標識符
- 組成程序集的文件列表
- 一個指示程序集中內容在哪里的地圖
- 關於引用的其他程序集的信息
- 類型元數據部分包含該程序集中定義的所有類型的信息。這些信息包含關於每個類型要知道的所有事情
- CIL部分包含程序集的所有中間代碼
- 資源部分是可選的,但可以包含圖形或語言資源
程序集代碼文件稱為模塊。盡管大部分程序集由單文件組成,但有些也有多個文件。對於有多個模塊的程序集,一個文件是主模塊(primary module ),而其他的是次要模塊(secondary modules )。
- 主模塊含有程序集的清單和到次要模塊的引用
- 次要模塊的文件名以擴展名.netmodule結尾
- 多文件程序集被視為一個單一單元。它們一起部署並一起定版
下圖闡明了一個帶次要模塊的多文件程序集。
程序集標識符
在.NET框架中,程序集的文件名不像在其他操作系統和環境中那么重要,更重要的是程序集的標識符(identity )。
程序集的標識符有4個組成部分,它們一起唯一標識了該程序集,如下所示。
- 簡單名 這只是不帶文件擴展名的文件名。每個程序集都有一個簡單名。它也被稱為程序集名或友好名稱(friendly name)
- 版本號 它由4個句點分開的整數字符串組成,形式為MajorVersion.MinorVersion.Build.Revision,例如2.0.35.9
- 文化信息 它是一個字符串,由2~5個字符組成,代表一種語言,或代表一種語言和一個國家或地區。例如,在美國使用英語的文化名是en-US。在中國使用中文,它是zh-CN。
- 公鑰 這個128字節字符串應該是生產該程序集的公司唯一的
公鑰是公鑰/私鑰對的一部分,它們是一組兩個非常大的、特別選擇的數字,可以用於創建安全的數字簽名。公鑰,顧名思義,可以被公開。私鑰必須被擁有者保護起來。公鑰是程序集標識符的一部分。我們稍后會在本章看到私鑰的使用。
程序集名稱的組成被包含在程序集清單中。下圖闡明了清單部分。
下圖展示了用在.NET文檔和書籍中的關於程序集標識符的一些術語。
強命名程序集
強命名(strongly named)程序集有一個唯一的數字簽名依附於它。強命名程序集比沒有強名稱的程序集更加安全,原因有以下幾點。
- 強名稱唯一標識了程序集。沒有其他人能創建一個與之有相同名稱的程序集,所以用戶可以確信該程序集來自於其聲稱的來源
- 沒有CLR安全組件來捕獲更改,帶強名稱的程序集的內容不能被改變
弱命名(weakly named )程序集是沒有被強命名的程序集。由於弱命名程序集沒有數字簽名, 它天生是不安全的。因為一根鏈的強度只和它最弱的一環相同,所以強命名程序集默認只能訪問其他強命名程序集(還存在一種方法允許“部分地相信調用者”,但本書不做闡述)。
程序員不產生強名稱。編譯器產生它,接受關於程序集的信息,並散列化這些信息以創建一個唯一的數據簽名依附到該程序集。它在散列處理中使用的信息如下:
- 組成程序集的字節序列
- 簡單名稱
- 版本號
- 文化信息
- 公鑰/私鑰對
在圍繞強名稱的命名法方面有一些差異。本書所指的“強命名的”常指的是“強名稱的”。 “弱命名的”有時指的是“非強命名的”或“帶簡單名稱的程序集”。
創建強命名程序集
要使用Visual Studio強命名一個程序集,必須有一份公鑰/私鑰對文件的副本。如果沒有密鑰文件,可以讓Visual Studio產生一個。可以實行以下步驟。
- 打開工程的屬性
- 選擇簽名頁
- 選擇Sign the Assembly復選框並輸入密鑰文件的位置或創建一個新的
在編譯代碼時,編譯器會生成一個強命名的程序集。編譯器的輸入和輸出如下圖
要創建強命名程序集還可以使用Strong Name工具(sn.exe ),這個工具在安裝Visual Studio 的時候會自動安裝。它是個命令行工具,允許程序員為程序集簽名,還能提供大量管理密鑰和簽名的其他選項。如果Visual Studio IDE還不符合你的要求,它能提供更多選擇。要使用Strong Name工具,可到網上查閱更多細節。
程序集的私有方式部署
在目標機器上部署一個程序就像在該機器上創建一個目錄並把應用程序復制過去一樣簡單。如果應用程序不需要其他程序集(比如DLL),或如果所需的DLL在同一目錄下,那么程序應該會就在它所在的地方良好工作。這種方法部署的程序集稱為私有程序集,而且這種部署方法稱為復制文件(XCopy)部署。
私有程序集幾乎可以被放在任何目錄中,而且只要它們依賴的文件都在同一目錄或子目錄下就足夠了。事實上,可以在文件系統的不同部分有多個目錄,每個目錄都有同樣的一組程序集,並且它們都會在它們各自不同的位置良好工作。
關於私有程序集部署的一些重要事情如下:
- 私有程序集所在的目錄被稱為應用程序目錄
- 私有程序集可以是強命名的也可以是弱命名的
- 沒有必要在注冊表中注冊組件
- 要卸載一個私有程序集,只要從文件系統中刪除它即可
共享程序集和GAC
私有程序集是非常有用的,但有時你會想把一個DLL放在一個中心位置,這樣一個單獨的復制就能被系統中其他的程序集共享。.NET有這樣的貯藏庫,稱為全局程序集緩存(GAC)。放進GAC的程序集稱為共享程序集。
關於GAC的一些重要內容如下:
- 只有強命名程序集能被添加到GAC
- GAC的早期版本只接受帶.dll擴展名的文件,現在也可以添加帶.exe擴展名的程序集了
- GAC位於Windows系統目錄的子目錄中。.NET4.0之前位於\Windows\Assembly中,從.NET4.0開始位於\Windows\Microsoft.NET\assembly中
把程序集安裝到GAC
當試圖安裝一個程序集到GAC時,CLR的安全組件首先必須檢驗程序集上的數字簽名是否有效。如果沒有數據簽名,或它是無效的,系統將不會把它安裝到GAC。
然而,這是個一次性檢査。在程序集已經在GAC內之后,當它被一個正在運行的程序引用時,不再需要進一步的檢査。
gacutil.exe命令行工具允許從GAC添加或刪除程序集,並列出GAC包含的程序集。它的3個最有用的參數標記如下所示。
/i
: 把一個程序集插人GAC/u
: 從GAC卸載一個程序集/l
: 列出GAC中的程序集
GAC內的並肩執行
在程序集部署到GAC之后,它就能被系統中其他程序集使用了。然而,請記住程序集的標識符由完全限定名稱的全部4個部分組成。所以,如果一個庫的版本號改變了,或如果它有一個不同的公鑰,這些區別指定了不同的程序集。
結果就是在GAC中可以有許多不同的程序集,它們有相同的文件名。雖然它們有相同的文件名,但它們是不同的程序集而且在GAC中完美地共存。這使不同的應用程序在同一時間很容易使用不同版本的同一DLL,因為它們是帶不同標識符的不同程序集。這被稱為並肩執行(side-by-side Execution )。
下圖闡明了GAC中4個不同的DLL,它們都有相同的文件名 MyLibary.dll。圖中,可以看出前3個來自於同一公司,因為它們有相同的公鑰,第4個來源不同,因為它有一個不同的公鑰。這些版本如下:
- 英文V1.0.0.0版,來白A公司
- 英文V2.0.0.0版,來自A公司
- 德文V1.0.0.0版,來自A公司
- 英文V1.0.0.0版,來自B公司
配置文件
配置文件含有關於應用程序的信息,供CLR在運行時使用。它們可以指示CLR去做這樣的事情,比如使用一個不同版本的DLL,或搜索程序引用的DLL時在附加目錄中查找。
配置文件由XML代碼組成,並不包含C#代碼。編寫XML代碼的細節超出了本書的范圍,但應當理解配置文件的目的以及它們如何使用。它們的一種用途是更新一個應用程序集以使用新版本的DLL。
例如,假設有一個應用程序引用了GAC中的一個DLL。在應用程序的清單中,該引用的標識符必須完全匹配GAC中程序集的標識符。如果一個新版本的DLL發布了,它可以被添加到GAC中,在那里它可以幸福地和老版本共存。
然而,應用程序仍然在它的清單中包括老版本DLL的標識符。除非重新編譯應用程序並使它引用新版本的DLL,否則它會繼續使用老版本。如果這是你想要的,那也不錯。
然而,如果你不想重新編譯程序但又希望它使用新的DLL,那么你可以創建一個配置文件告訴CLR去使用新的版本而不是舊版本。配置文件被放在應用程序目錄中。
下圖闡明了運行時過程中的對象。左邊的應用程序MyProgram.exe調用MyLibrary.dll的1.0.0.0版,如點化線箭頭所示。但應用程序有一個配置文件,而它指示CLR加載2.0.0.0版。注意配置文件的名稱由執行文件的全名(包括擴展名)加上附加擴展名.config組成。
延遲簽名
公司小心地保護它們官方的公鑰/私鑰對是非常重要的,否則,如果不可靠的人得到了它,就可以發布偽裝成該公司的代碼。為了避免這種情況,公司顯然不能允許自由訪問含有它們的公鑰/私鑰對的文件。在大公司中,最終程序集的強命名經常在開發過程的最尾部由特殊的有密鑰訪問權限的小組執行。
可是,由於個別原因,這會在開發和測試過程中導致問題。首先,由於公鑰是程序集標識符的4個部分之一,所以直到提供了公鑰它才能被設置。而且,弱命名的程序集不能被部署到GAC。開發人員和測試人員都需要有能力編譯和測試該代碼,並使用它將要被部署發布的方式,包括它的標識符和在GAC中的位置。
為了允許這個,有一種修改了的賦值強命名的形式,稱為延遲簽名(delayed signing)或部分簽名(partial signing),它克服了這些問題,而且沒有釋放對私鑰的訪問。
在延遲簽名中,編譯器只使用公鑰/私鑰對中的公鑰。然后公鑰可以被放在完成的程序集的標識符清單中。延遲簽名還使用一個為0的塊保留數字簽名的位賈。
要創建一個延遲簽名的程序集,必須做兩件事情。第一,創建一個密鑰文件的副本,它只有公鑰而不是公鑰/私鑰對。下一步,為程序集范圍內的源代碼添加一個名稱為 DelaySignAttribute 的附加特性,並把它的值設為true。
下圖展示了生成一個延遲簽名程序集的輸人和輸出。注意圖中下面的內容。
- 在輸人中,DelaySignAttribute 定位於源文件中,而且密鑰文件只含有公鑰
- 在輸出中,在程序集的底部有一個數字簽名的保留空間
如果你試圖部署延遲簽名的程序集到GAC,CLR不會允許,因為它不是強命名的。要在這台機器上部署它,必須首先使用命令行指令取消在這台機器上的GAC簽名確認,只針對這個程序集,並允許它被裝在GAC中。要做到這點,從Visual Studio命令提示中執行下面的命令。
sn -vr MyAssembly.dll
現在,你已經看到弱命名程序集、延遲簽名程序集和強簽名程序集。下圖總結了它們的結構區別。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">