程序集
=============================================================================================== ----------------------------------------------------------------------1 什么是程序集? ---------------------------------------------------------------------- DLL的版本問題,程序不知道到底該使用哪個(gè)DLL版本,從而產(chǎn)生中斷。 DLL-Hell----〉與DLL相關(guān)的問題 .NET對DLL-Hell及其所有問題的答案是使用程序集 程序集-是自我描述的安裝單元,由一個(gè)或多個(gè)文件組成,一個(gè)程序集可以是一個(gè)包括元數(shù)據(jù)的DLL或EXE,也可以由多個(gè)文件組成。 另一優(yōu)點(diǎn)是可以使私有或共享的。 私有程序集和共享程序集有很大的區(qū)別 版本沖突問題必須在開發(fā)階段解決 程序集的特性: 1 程序集是自我描述的; 2 版本的互相依賴性在程序集的清單中進(jìn)行了記錄 3 程序集可以進(jìn)行并行加載 ,同一個(gè)DLL的不同版本可以在同一個(gè)系統(tǒng)上同時(shí)使用 4 應(yīng)用程序使用應(yīng)用程序域來確保其獨(dú)立性(Application Domain) 5 安裝非 常簡單,只要復(fù)制一個(gè)程序集中的所有文件,一個(gè)xcopy命令就夠了--無干涉部署(No-touch Deployment)
應(yīng)用程序集和應(yīng)用程序域
----------------------- | ---------------------------- 進(jìn)程一 進(jìn)程二 ____________ | ____________ 應(yīng)用程序域A 應(yīng)用程序域B | ...... ____________ | ____________ ------------------------- ---------------------------- 進(jìn)程之間有邊界,應(yīng)用程序域存在于某個(gè)進(jìn)程中,之間也有邊界,處在不同應(yīng)用程序域中的應(yīng)用程序不能互相訪問 應(yīng)用程序域可以互相包含
------------------------------------------ ====================== 進(jìn)程〉應(yīng)用程序域〉程序集〉對象 ====================== ------------------------------------------ 應(yīng)用程序域之間可以通信,或使用代理 AppDomain類用于創(chuàng)建和中斷應(yīng)用程序域,加載和卸載程序集和類,枚舉域中的程序集和線程 例如: 1--創(chuàng)建一個(gè)控制臺應(yīng)用程序AssemblyA 2--第二個(gè)應(yīng)用程序DomainTest加載AssemblyA.exe程序集 ///////////////////////////////////////////////////////////////////////////////////////// using System; namespace Wrox.ProCSharp.Assemblies.AppDomains { class Class1 { public Class1(int val1,int val2) {//構(gòu)造函數(shù),具有兩個(gè)參數(shù),以了解如何創(chuàng)建AppDomain類的實(shí)例 Console.WriteLine("Constructor with the values {0},{1}"+ "in domain {2} called",val1,val2, AppDomain.CurrentDomain.FriendlyName); } [STAThread] static void Main(string[] args) {//在Main中,從而知道什么時(shí)候調(diào)用Console.WriteLine Console.WriteLine("Main in domain {0} called", AppDomain.CurrentDomain.FriendlyName); } } } //////// ///////////////////////////////////////////////////////////////////////////////// using System; namespace Wrox.ProCShap.Assemblies.AppDomains { class Test { [STAThread] static void Main(string[] args) { AppDomain currentDomain = AppDomain.CurrentDomain; Console.WriteLine(currentDomain.FriendlyName); //使用AppDomain類的FriendlyName屬性顯示當(dāng)前域的名稱
////FLAG//// AppDomain.secondDomain = AppDomain.CreateDomain("New AppDomain"); //創(chuàng)建一個(gè)新的應(yīng)用程序域New AppDomain
secondDomain.ExecuteAssembly("AssemblyA.exe"); //把程序集AssemblyA加載到新域中,通過調(diào)用ExecuteAssembly來調(diào)用Main()方法 } } }
在啟動(dòng)DomainTest.exe前,必須把程序集AssemblyA.exe復(fù)制到DomainTest.exe所在的目錄下,這樣程序才能找到 這個(gè)程序集,不能添加AssemblyA.exe程序集的引用,因?yàn)樵赩S.NET中,只能給以DLL格式存儲(chǔ)的程序集添加引用,不支持 EXE格式,但是EXE格式的程序集可以在命令行上執(zhí)行,如果找不到這個(gè)程序集就會(huì)拋出System.IO.FileNotFoundExection異常 結(jié)果:Main in domain New AppDomain called
在上面第二個(gè)程序表明的FLAG處可以改為:再創(chuàng)建一個(gè)實(shí)例,用于替代Main()方法 ///////////////////////////////////////////////////////////////////////////////////////// AppDomain secondDomain = AppDomain.CreateDomain("New AppDomain");
secondDomain.CreateInstance("AssemblyA" ,"Wrox.ProCShap.Assemblies.AppDomains.Class1", true,System.Reflection.BindingFlags.CreateInstance,null,new object[]{7,3} null,null,null); ///////////////////////////////////////////////////////////////////////////////////////// //1-程序集名;2-應(yīng)實(shí)例化的類;3-true表示不區(qū)分大小寫;4-邦定標(biāo)志枚舉值,指定應(yīng)調(diào)用的構(gòu)造函數(shù); 得到的輸出為:Constructor with the values 7,3 in domain New AppDomain called
在運(yùn)行期間主應(yīng)用程序域會(huì)自動(dòng)創(chuàng)建, ASP.NET為每個(gè)運(yùn)行在WEB服務(wù)器上的Web應(yīng)用程序創(chuàng)建一個(gè)應(yīng)用程序域, Internet Explorer創(chuàng)建運(yùn)行托管控件的應(yīng)用程序域, 卸載應(yīng)用程序域只能通過中斷應(yīng)用程序域來進(jìn)行
----------------------------------------------------------------------2 程序集的結(jié)構(gòu) ---------------------------------------------------------------------- 程序集由=描述它的元數(shù)據(jù)(程序集元數(shù)據(jù))+描述導(dǎo)出類型和方法的類型元數(shù)據(jù)+MSIL代碼+資源 存在于一個(gè)文件中或分布在多個(gè)文件中 例如: 1 程序集由一個(gè)文件組成:Component.dll 2 Component.dll=描述它的元數(shù)據(jù)+描述導(dǎo)出類型和方法的類型元數(shù)據(jù)+MSIL代碼, 資源不在其中 這個(gè)程序集使用了一個(gè)圖:Pictrue.jpeg,該圖沒有嵌入在dll中,而是在程序集的元數(shù)據(jù)中引用 程序集的原數(shù)據(jù)還引用了一個(gè)模塊:Util.netmodule,該模塊只包含一個(gè)類的類型元數(shù)據(jù)和MSIL代碼 不包含程序集的元數(shù)據(jù),所以這個(gè)模塊沒有版本信息,也不能單獨(dú)安裝 這3個(gè)文件構(gòu)成了程序集,這個(gè)程序集是一個(gè)安裝單元,還可以在另外一個(gè)文件中放置程序集清單 Component.dll Util.netmodule ________________________ ____________ Picture.jpeg<------- 程序集元數(shù)據(jù) -------〉 類型元數(shù)據(jù) __________ -------〉 IL代碼 資源 類型元數(shù)據(jù) IL代碼
程序集的清單 元數(shù)據(jù)的一部分,描述了程序集和引用它所需的所有信息,并列出,了所有的依賴關(guān)系 清單=標(biāo)識(名稱,版本,文化,公鑰)+屬于該程序集的一個(gè)文件列表+ 引用程序集的列表+一組許可請求--運(yùn)行的許可證+ 導(dǎo)出的類型(當(dāng)它們在一個(gè)模塊中定義,該模塊在程序集中引用,否則不屬于清單)
--------------------------------------------- ====================== 命名空間,程序集和組件 1 命名空間完全獨(dú)立于程序集 2 一個(gè)程序集中可以有不同的命名空間 3 一個(gè)命名空間也可以分布在多個(gè)程序集中 4 命名空間只是類名的一種擴(kuò)展,它屬于類名的范疇 ====================== ---------------------------------------------
====================== 私有程序集和共享程序集 在使用共享程序集時(shí),程序集必須是唯一的,名稱唯一(強(qiáng)名),該名稱的一部分是一個(gè)強(qiáng)制的版本號,如第三方控件 ====================== ====================== 查看程序集 命令行工具:ILDASM 或 FILE-->OPEN-->打開程序集 ====================== ====================== 構(gòu)建程序集 1-可以創(chuàng)建模塊:csc /target:module hello.cs 模塊是一個(gè)沒有程序集特性的DLL,可以添加到程序集中 創(chuàng)建了hello.netmodule 2-生成一個(gè)程序集B.DLL ,它包含模塊A.netmodule: csc /target:library /addmodule:A.netmodule /out:B.dll 3-模塊的作用1是可以更快的啟動(dòng)程序集,因?yàn)椴⒉皇撬蓄惗荚谝粋€(gè)文件中 模塊只在需要時(shí)加載 2是是否需要使用多種編程語言來創(chuàng)建一個(gè)程序集:一個(gè)模塊用VB.net,一個(gè)模塊用C#,這兩個(gè)模塊都包含在一個(gè)程序集中 4-使用VS創(chuàng)建程序集 VS2003不支持直接創(chuàng)建模塊 創(chuàng)建一個(gè)項(xiàng)目時(shí),系統(tǒng)自動(dòng)生成源文件AssemblyInfo.cs,在該文件中可以使用一般的源代碼編輯器配置程序集的屬性 [assembly]和[module]是程序集的全局屬性
System.Reflection命名空間中的類: AssemblyCompany ---指定公司名 AssemblyConfiguration --指定建立信息,例如零售或調(diào)試信息 AssemblyCopyright/Assembly Trademark --包含版權(quán)和商標(biāo)信息 AssemblyDefaultAlias --如果程序集名稱不容易理解,如動(dòng)態(tài)創(chuàng)建程序集名稱時(shí)的GUID,就可以使用該屬性,指定一個(gè)別名 AssemblyDescription --描述程序集或產(chǎn)品,如果查看可執(zhí)行文件的屬性,這個(gè)值就會(huì)顯示為Comments AssemblyProduct --指定了屬于該程序集的產(chǎn)品名稱 AssemblyInfomationalVersion --在引用程序集時(shí),這個(gè)屬性不用于版本檢查,僅用于版本信息,非常適用于指定使用多個(gè)程序集的應(yīng)用程序的版本,打開可執(zhí)行程序的屬性,這個(gè)值就顯示為Product Version AssemblyTitle --是程序集的描述性名稱,可以包括空格,查看屬性時(shí)顯示為Description
System.Runtime.CompilerServices命名空間中的類: AssemblyCulture --程序集的文化背景,如en-US AssemblyDelaySign/AssemblyKeyFile/AssemblyKeyName --用于創(chuàng)建共享程序集的強(qiáng)名 AssemblyVersion --指定程序集的版本號,版本問題在共享程序集中具有非常重要的地位 ======================
====================== 跨語言支持 .NET 使用通用類型系統(tǒng)Common Type System-CTS-定義了如何在.net中定義值類型和引用類型,以及這些類型的內(nèi)存布局 但CTS沒有確保在任何語言中定義的類型都可以用于其它語言。 這應(yīng)是公共語言規(guī)范Common Language Specification-CLS 的任務(wù)。 CLS定義了.net語言必須支持的最低要求 ======================
CTS=common type system=通用類型系統(tǒng) CLS=common language specification=公共語言規(guī)范 ====================== 例如: ********************************************************************************** Visual C++編寫的基類HelloMCPP/ 繼承于HelloMCPP的類HelloVB/ 繼承于HelloVB的類HelloCSharp HelloMCPP HelloVB HelloCSharp
+Hello() +Hello() +Hello() +Hello2() +Add() +Add() +Add() +Main() ********************************************************************************** --使用VS2003創(chuàng)建一個(gè)VC的類-class library(.net) 1>HelloMCPP //HelloMCPP.h #pragma once #include <stdio.h> using namespace System; namespace Wrox {namespace ProCSharp {namespace Assemblies {namespace CrossLanguage {public _gc class HelloMCPP //_gc 標(biāo)記類HelloMCPP,使類成為一個(gè)托管類 {public: virtual void Hello() {Console::WriteLine(S"Hello,ManagedC++");}//S作為字符串的前綴,托管的字符串就會(huì)寫入程序集,并用ldstr推入堆棧 virtual void Hello2() ] {printf("Hello,calling native code \n");}
int Add(int val1,int val2) {return val1+val2;} }; }}}} ********************************************************************************** --使用VS2003創(chuàng)建一個(gè)VB.net的類-class library --打開項(xiàng)目屬性,在Root Namespace 中將項(xiàng)目的根命名空間改為Wrox.ProCSharp.Assemblies.CrossLanguage ,這樣就改變了類的命名空間 --添加對HellpMCPP的引用:Project-->Add Reference 給項(xiàng)目添加引用就是將引用的程序集復(fù)制到VB.net項(xiàng)目的輸出目錄上(/bin),然后對原引用程序集的改變就是獨(dú)立的 2>HelloVB public class HelloVB Inherits HelloMCPP
public Overrides Sub Hello() MyBase.Hello() 'MyBase關(guān)鍵字代表基類,此處調(diào)用基類的方法 Console.WriteLine("Hello,VB.NET") End Sub
public Shadows Function Add(ByVal val1 as integer,_ ByVal val2 as integer) as integer 'Shadows關(guān)鍵字用來隱藏基類的方法Add(),因?yàn)樵诨愔蠥dd()不是虛擬(virtual)的 '不能被重寫 return val1+val2 End Function End class ********************************************************************************** --創(chuàng)建一個(gè)C#控制臺應(yīng)用程序,添加對HelloVB和HelloMCPP的引用 3>HelloCSharp using System; namespace Wrox.ProCSharp.Assemblies.CrossLanguage { public class HelloCSharp:HelloVB { public HelloCSharp() {}
public override void Hello() { base.Hello(); Console.WriteLine("Hello,C#"); }
public new int Add(int val1,int val2) { return val1+val2; }
[STAThread]
public static void Main() { HelloCSharp hello new HelloCSharp(); hello.Hello(); } } } ********************************************************************************** 最后控制臺應(yīng)用程序的輸出如下: Hello,Managed C++ Hello,VB.NET Hello,C# ************************* 因?yàn)樗械?net語言都生成MSIL代碼,所有的語言都使用.NET FRAMEWORK中的類,所以在性能上是沒有區(qū)別的 但仍有一些小的差別,首先,由于語言的不同,某些語言支持的數(shù)據(jù)類型其它語言不支持 其次,生成的MSIL代碼仍有差別 在默認(rèn)配置下,VB.NET上的執(zhí)行比較安全,C#上的執(zhí)行比較快,C#也更靈活 ************************* 在基類中定義的方法在都可以在派生類中調(diào)用,如果方法的參數(shù)是System.UInt32數(shù)據(jù)類型,就不能在VB.NET中使用它 ,因?yàn)閂B.NET不支持無符號數(shù)據(jù) 無符號的數(shù)據(jù)類型與CLS不兼容,.net的語言不必支持這種數(shù)據(jù)類型 ********************************************************************************** .NET的語言 .NET consumer工具 只使用.NET FRAMEWORK中的類,不能創(chuàng)建用于其它語言的.NET類; 可以使用任何與CLS兼容的類 .NET extends工具 可以滿足客戶的要求,可以繼承任何與CLS兼容的.NET類; 定義了可以由客戶使用的新CLS兼容類 C++,VB.NET,C#都是.NET extender工具,使用這些語言可以創(chuàng)建CLS兼容類
********************************************************************************** CLSCompliant屬性 利用它可以把程序集標(biāo)記為與CLS兼容,這樣可以確保這個(gè)程序集中的類能用于所有的.NET consumer工具; 在公共方法或受保護(hù)的方法中使用與CLS不兼容的數(shù)據(jù)類型時(shí),編譯器會(huì)給出警告; 在私有方法中使用什么樣的數(shù)據(jù)類型則不重要,因?yàn)樵陬惖耐獠渴褂闷渌Z言時(shí),根本就不能訪問私有方法; 當(dāng)在公共方法和受保護(hù)的方法中使用與CLS不兼容的類型時(shí),為了讓編譯器發(fā)出警告,可以設(shè)置程序集中的屬性 CLSCompliant,把這個(gè)屬性添加到AssemblyInfo.cs中: [assembly:System.CLSCompliant(true)] 這樣,在程序集中定義的所有類型和公共方法就都是兼容的,當(dāng)參數(shù)的數(shù)據(jù)類型是不兼容的UINT時(shí),編譯器就會(huì)發(fā)出如下 警告:error CS3001:Argument type uint is not CLS-compliant 把程序集標(biāo)記為兼容時(shí),仍可以定義不兼容的方法,如果要重寫某些方法,使其參數(shù)是兼容和不兼容的數(shù)據(jù)類型,就必須把 類中的不兼容的方法的CLSCompliant屬性設(shè)置為false CLSCompliant 屬性可以用到類型,方法,屬性,字段和事件上: [CLSCompliant(false)] void Method(uint i) {//...... ********************************************************************************** CLS規(guī)則 程序集和與CLS兼容的要求: 方法原形中的所有類型都必須與CLS兼容; 數(shù)組元素的元素類型必須與CLS兼容。數(shù)組的第一個(gè)元素的下標(biāo)必須是0; CLS兼容類必須繼承與CLS兼容類,當(dāng)然,System.Object 是與CLS兼容的; 在CLS兼容類中,方法名士不區(qū)分大小寫的,兩個(gè)方法不能僅根據(jù)其名稱中字母的大小寫來區(qū)分; 枚舉的類型必須是Int16,Int32,Int64,其它類型的枚舉都是不兼容的; 上述要求只適用于公共成員和受保護(hù)的成員,私有方法則無需考慮這些要求,它們可以使用不兼容的類型, 而程序集仍然是兼容的 還應(yīng)該遵循更一般的命名約定 C# VB.NET 更一般 int integer Int32 long long Int64 float single Single 在利用CLS規(guī)范和規(guī)則進(jìn)行編譯時(shí),很容易創(chuàng)建出可以用于多種語言的組件,不需要使用所有的.NET FrameWork語言來測試該組件 ********************************************************************************** 全局程序集緩存 Global Assembly Cache 可全局使用的程序集的緩存 大多數(shù)共享程序集都安裝在這個(gè)緩存中,其中也安裝了一些私有程序集 如果私有程序集使用本機(jī)圖像生成器編譯為本機(jī)代碼,編譯好的本機(jī)代碼也會(huì)存儲(chǔ)在這個(gè)緩存中 ********************************************************************************** 本機(jī)圖像生成器 native image generator Ngen.exe 可以在安裝期間把IL代碼編譯為本機(jī)代碼,這樣程序啟動(dòng)就比較快,因?yàn)椴辉傩枰谶\(yùn)行時(shí)進(jìn)行編譯 Ngen工具在本機(jī)圖像緩存中安裝本機(jī)圖像,本機(jī)圖像緩存是全局程序集緩存的一部分
********************************************************************************** 全局程序集緩存查看器 全局程序緩存可以使用shfusion.dll來顯示,它是一個(gè)Windows外殼擴(kuò)展程序,可以查看和處理緩存的內(nèi)容 Windows外殼擴(kuò)展程序是一個(gè)與Windows資源管理器集成的COM DLL 用戶啟動(dòng)資源管理器,進(jìn)入<windir>/assembly目錄即可 可以查看全局程序集的名稱,類型,版本,文化和公鑰標(biāo)記,通過查看全局程序集的類型可以確定程序集是否 是使用本機(jī)圖像生成器安裝的 在該目錄下,有GAC和NativeImages_<runtime version>目錄,分別是共享程序集的目錄和編譯為本機(jī)代碼的程序集
********************************************************************************** 全局程序集緩存工具 gacutil.exe 全局程序集緩存查看器可以查看和刪除程序集,但不能在腳本代碼中使用該工具,例如創(chuàng)建安裝程序。 gacutil.exe 可以使用命令行安裝,卸載和顯示程序集 選項(xiàng): gacutil /l --顯示程序集緩存中的所有程序集 gacutil /i mydll --把共享程序集mydll安裝到程序集緩存上 gacutil /u mydll --卸載程序集mydll gacutil /ungen mydll --從本機(jī)圖像緩存中卸載程序集 ********************************************************************************** 創(chuàng)建共享程序集 程序集可以由一個(gè)應(yīng)用程序使用,在默認(rèn)下部共享程序集,在使用私有程序集時(shí),不需要考慮共享時(shí)需要考慮的任何要求 共享程序集名:必須是全局唯一的,1要保護(hù)該名稱,2要使得其他人不能使用這個(gè)名稱創(chuàng)建程序集 COM使用全局唯一標(biāo)識符GUID只解決了第一個(gè)問題,第二個(gè)問題仍然沒有解決,每個(gè)人 都可以盜用這個(gè)GUID,用相同的標(biāo)識符創(chuàng)建不同的對象 這兩個(gè)問題使用.NET程序集的強(qiáng)名都可以解決 強(qiáng)名由以下項(xiàng)目組成: 程序集本身的名稱 版本號 公鑰,保證強(qiáng)名是獨(dú)一無二的,并且保證引用的程序集不能被另一個(gè)源替代 文化 共享程序集必須有一個(gè)強(qiáng)名,來唯一的標(biāo)識該程序集 每個(gè)程序集不能有新的公鑰,但可以在公司中有這樣一個(gè)公鑰,這樣該密鑰就唯一的標(biāo)識了公司的程序集 但是 ,這個(gè)密鑰不能用作信任密鑰,程序集可以利用Authenticode簽名來建立信任關(guān)系, Authenticode中的密鑰可以與強(qiáng)名中使用的密鑰不同
********************************************************************************** 公鑰的加密 對稱加密:使用同一個(gè)密鑰進(jìn)行加密和解密 公鑰/私鑰加密:使用一個(gè)公鑰加密,使用對應(yīng)的私鑰解密/使用一個(gè)私鑰加密,使用對應(yīng)的公鑰解密 成對創(chuàng)建公鑰和私鑰,公鑰可以任何人使用,甚至可以放在web站點(diǎn)上,但私鑰必須安全的加鎖 Sarah-->one email -->Julian,除了Julian外的人都不能看 使用Julian的公鑰加密,Julian打開該email,并使用他秘密存儲(chǔ)的私鑰解密 但還有一個(gè)問題,Julian不能確保email是Sarah發(fā)來的,任何人都可以使用Julian的公鑰加密發(fā)送email給他 解決辦法是黨Sarah發(fā)送email給Julian時(shí),使用Julian的公鑰加密郵件之前他添加了自己的簽名,再使用自己的 私鑰加密該簽名,然后使用Julian的公鑰加密email,這個(gè)簽名可以使用Sarah的公鑰來解密,而Julian可以 訪問Sarah的公鑰,在解密了簽名后,Julian就可以確定是Sarah發(fā)送了email
********************************************************************************** 將公鑰/私鑰應(yīng)用于程序集 創(chuàng)建共享組件,必須使用公鑰/私鑰對 編譯器把公鑰寫入程序集清單,創(chuàng)建屬于該程序集的所有文件的散列表--用私鑰標(biāo)記這個(gè)散列表 私鑰不存儲(chǔ)在程序集中,確保沒有人可以修改這個(gè)程序集,簽名可以使用公鑰來驗(yàn)證 在開發(fā)過程中,客戶程序集必須引用 共享程序集。編譯器把引用程序集的公鑰寫入客戶程序集的清單中 要減少存儲(chǔ)量,就不應(yīng)把公鑰寫入客戶程序集的清單,而應(yīng)寫入公鑰標(biāo)記,公鑰標(biāo)記是公鑰散列表中的最后8位字節(jié),且是唯一的 在運(yùn)行期間加載共享程序集時(shí)(如果客戶程序集是使用本機(jī)圖像生成器安裝的,則應(yīng)在安裝期間加載), 共享程序集的散列表可以使用存儲(chǔ)在客戶程序集中的公鑰來驗(yàn)證,除了私鑰的主人外其他人都不能修改共享程序集 例如: 銷售商A創(chuàng)建了一個(gè)組件Math,在客戶機(jī)上引用該組件,黑客的組件就無法替代它,只有私鑰的主人才能用 新版本來替換原來的共享組件,保證了其完整性
********************************************************************************** 創(chuàng)建共享程序集 例子 1 建立一個(gè)Visual C# Class Library 項(xiàng)目 SharedDemo,把命名空間改為Wrox.ProCSharp.Assemblies.Sharing 類名改為SimpleShared using System; using System.Collection.Specialized; using System.IO;
namespace Wrox.ProCSharp.Assemblies.Sharing { public class SharedDemo { private StringCollection quotes;//類的構(gòu)造函數(shù)將文件的所有行都讀到其中 //文件名作為參數(shù)傳遞到構(gòu)造函數(shù) private Random random; public SharedDemo(string filename) { quotes = new StringCollection(); Stream stream =File.OpenFile(filename); StreamReader streamReader = new StreamReader(stream); string quote; while((quote=streamReader.ReadLine())!=null) { quotes.Add(quote); } streamReader.Close(); stream.Close(); random = new Random(); } public string GetQuoteOfTheDay() //返回這個(gè)集合的一個(gè)隨機(jī)字符串 { int index = random.Next(1,quotes.Count); retrun quotes[index]; } } } ************************* * a>創(chuàng)建強(qiáng)名稱 *--程序集中有了公鑰 ************************* 要共享這個(gè)組件,需要一個(gè)強(qiáng)名稱,要?jiǎng)?chuàng)建這個(gè)名稱可以使用強(qiáng)名稱工具sn: sn -k mykey.snk 強(qiáng)名稱工具生成和編寫一個(gè)公鑰/私鑰對,并把該密鑰對寫到文件中,此處的文件是mykey.snk,現(xiàn)在可以 在向?qū)傻奈募嗀ssemblyinfo.cs中設(shè)置屬性AssemblyKeyFile,該屬性可以設(shè)置為密鑰文件的絕對路徑,也可以 設(shè)置為密鑰文件的相對路徑%ProjectDirectory\obj\<configuration>目錄,所以../../mykey.snk引用項(xiàng)目目錄中的 一個(gè)密鑰,在開始建立一個(gè)項(xiàng)目時(shí),該密鑰安裝到Crypto Service Provider(CSP)中,如果該密鑰已經(jīng)安裝到CSP中,就可以 使用AssemblyKeyName屬性 下面是對Assemblyinfo.cs的修改 [assembly:AssemblyDelaySign(false)] [assembly:AssemblyKeyFile("../../mykey.snk")] [assembly:AssemblyKeyName("")] 在重新建立該文件后,使用ildasm查看該程序集,則該程序集的清單中應(yīng)有一個(gè)公鑰
************************* * b>安裝共享程序集 * ************************* 使用全局程序集緩存工具gacutil及其/I選項(xiàng)把它安裝到全局程序集緩存中: gacutil /i SharedDemo.dll 可以使用全局程序集緩存查看器檢查共享程序集的版本,看看它是否安裝成功
************************* * c>使用共享程序集 * ************************* 創(chuàng)建一個(gè)C#控制臺應(yīng)用程序Client,不是把新項(xiàng)目添加到原來的解決方案中,而是創(chuàng)建一個(gè)新的解決方案 這樣在重新建立客戶時(shí),就不會(huì)重新建立該共享程序集了。 以引用私有程序集方式引用程序集SimpleShared.dll,使用菜單Project|Add Reference 有了共享程序集,引用屬性CopyLocal就可以設(shè)置為false,這樣,共享程序集就不會(huì)復(fù)制到輸出文件的目錄下 而會(huì)從全局程序集緩存中加載 下面是客戶機(jī)應(yīng)用程序的代碼: using System; namespace Wrox.ProCSharp.Assemblies.Sharing { class Client { [STAThread] static void Main(string[] args) { SharedDemo quotes = new SharedDemo(@"C:\ProCSharp\Assemblies\Quotes.txt"); for(int i=0;i<3;i++) { Console.WriteLine(quotes.GetQuoteOfTheDay()); Console.WriteLine(); } } } } ********************************************************************************** 公鑰的標(biāo)記也可以使用強(qiáng)名稱工具sn在共享程序集中查看:sn -T會(huì)顯示程序集中的公鑰標(biāo)記 sn -Tp顯示標(biāo)記和公鑰 ********************************************************************************** 程序集的延遲簽名 公司的私鑰應(yīng)安全存儲(chǔ),大多數(shù)公司不允許所有的開發(fā)人員訪問私鑰,只有幾個(gè)有安全權(quán)限的人才能訪問 這就是程序集的簽名可以以后(例如發(fā)布前)添加的原因 全局程序集屬性AssemblyDelaySign設(shè)置為true時(shí),簽名就不會(huì)存儲(chǔ)在程序集中,但保留了足夠的空間,以便 以后添加。 但是不使用密鑰就不能測試程序集,在全局程序集緩存中安裝它 但可以使用臨時(shí)密鑰進(jìn)行測試,以后再用真正的密鑰代替這個(gè)臨時(shí)密鑰 程序集的延時(shí)簽名需要執(zhí)行以下步驟: 1>使用sn創(chuàng)建一個(gè)公鑰/私鑰對,生成文件mykey.snk,包含公鑰和私.snsn -k mykey.snk 2>提取公鑰,使之可以用于開發(fā)人員。選項(xiàng)-p提取密鑰文件的公鑰,文件mypublickey.snk僅包含公鑰 sn -p mykey.snk mypublickey.snk 公司中所有開發(fā)人員都可以使用這個(gè)密鑰文件mypublickey.snk 在文件AssemblyInfo.cs中設(shè)置AssemblyDelaySign和AssemblyKeyFile屬性 [assembly:AssemblyDelaySign(true)] [assembly:AssemblyKeyFile("../../mypublickey.snk")] 3>關(guān)閉簽名的驗(yàn)證功能,因?yàn)槌绦蚣瘺]有包含簽名 sn -Vr ShareDemo.dll 4>在發(fā)布之前,程序集可以用sn工具重新簽名 -R選項(xiàng)用于對以前已簽名或延遲簽名的程序集進(jìn)行重新簽名 sn -R MyAssembly.dll mykey.snk 注意:簽名的驗(yàn)證功能只能在開發(fā)過程中關(guān)閉,不經(jīng)過驗(yàn)證是不能發(fā)布程序集的,因?yàn)檫@個(gè)程序集可能被懷有惡意的程序集代替 **********************************************************************************
程序集(assembly)是包含編譯好的、面向.NET Framework的代碼的邏輯單元。程序集是完全自我描述性的,也是一個(gè)邏輯單元而不是物理單元,它可以存儲(chǔ)在多個(gè)文件中(動(dòng)態(tài)程序集的確存儲(chǔ)在內(nèi)存中,而不是存儲(chǔ)在文件中)。如果一個(gè)程序集存儲(chǔ)在多個(gè)文件中,其中就會(huì)有一個(gè)包含入口點(diǎn)的主文件,該文件描述了程序集中的其他文件。
注意可執(zhí)行代碼和庫代碼使用相同的程序集結(jié)構(gòu)。惟一的區(qū)別是可執(zhí)行的程序集包含一個(gè)主程序入口點(diǎn),而庫程序集則不包含。
程序集的一個(gè)重要特性是它們包含的元數(shù)據(jù)描述了對應(yīng)代碼中定義的類型和方法。程序集也包含描述程序集本身的元數(shù)據(jù),這種程序集元數(shù)據(jù)包含在一個(gè)稱為程序集清單的區(qū)域中,可以檢查程序集的版本及其完整性。
注意:
ildasm是一個(gè)基于Windows的實(shí)用程序,可以用于檢查程序集的內(nèi)容,包括程序集清單和元數(shù)據(jù)。第15章將介紹ildasm。
程序集包含程序的元數(shù)據(jù),表示調(diào)用給定程序集中的代碼的應(yīng)用程序或其他程序集不需要指定注冊表或其他數(shù)據(jù)源,以便確定如何使用該程序集。這與以前的COM有很大的不同,以前,組件的GUID和接口必須從注冊表中獲取,在某些情況下,方法和屬性的詳細(xì)信息也需要從類型庫中讀取。
把數(shù)據(jù)分散在3個(gè)以上的不同位置上,可能會(huì)出現(xiàn)信息不同步的情況,從而妨礙其他軟件成功地使用該組件。有了程序集后,就不會(huì)發(fā)生這種情況,因?yàn)樗械脑獢?shù)據(jù)都與程序的可執(zhí)行指令存儲(chǔ)在一起。注意,即使程序集存儲(chǔ)在幾個(gè)文件中,數(shù)據(jù)也不會(huì)出現(xiàn)不同步的問題。這是因?yàn)榘绦蚣肟诘奈募泊鎯?chǔ)了其他文件的細(xì)節(jié)、散列和內(nèi)容,如果一個(gè)文件被替換,或者被塞滿,系統(tǒng)肯定會(huì)檢測出來,并拒絕加載程序集。
程序集有兩種類型:共享程序集和私有程序集。
1.4.1 私有程序集
私有程序集是最簡單的一種程序集類型。私有程序集一般附帶在某些軟件上,且只能用于該軟件中。附帶私有程序集的常見情況是,以可執(zhí)行文件或許多庫的方式提供應(yīng)用程序,這些庫包含的代碼只能用于該應(yīng)用程序。
系統(tǒng)可以保證私有程序集不被其他軟件使用,因?yàn)閼?yīng)用程序只能加載位于主執(zhí)行文件所在文件夾或其子文件夾中的程序集。
用戶一般會(huì)希望把商用軟件安裝在它自己的目錄下,這樣軟件包沒有覆蓋、修改或加載另一個(gè)軟件包的私有程序集的風(fēng)險(xiǎn)。私有程序集只能用于自己的軟件包,這樣,用戶對什么軟件使用它們就有了更多的控制。因此,不需要采取安全措施,因?yàn)檫@沒有其他商用軟件用某個(gè)新版本的程序集覆蓋原來的私有程序集的風(fēng)險(xiǎn)(但軟件是專門執(zhí)行懷有惡意的損害性操作的情況除外)。名稱也不會(huì)有沖突。如果私有程序集中的類正巧與另一個(gè)人的私有程序集中的類同名,是不會(huì)有問題的,因?yàn)榻o定的應(yīng)用程序只能使用私有程序集的名稱。
因?yàn)樗接谐绦蚣耆亲院降?,所以安裝它的過程就很簡單。只需把相應(yīng)的文件放在文件系統(tǒng)的對應(yīng)文件夾中即可(不需要注冊表項(xiàng)),這個(gè)過程稱為“0影響(xcopy)安裝”。
1.4.2 共享程序集
共享程序集是其他應(yīng)用程序可以使用的公共庫。因?yàn)槠渌浖梢栽L問共享程序集,所以需要采取一定的保護(hù)措施來防止以下風(fēng)險(xiǎn):
● 名稱沖突,另一個(gè)公司的共享程序集執(zhí)行的類型與自己的共享程序集中的類型同名。因?yàn)榭蛻魴C(jī)代碼理論上可以同時(shí)訪問這些程序集,所以這是一個(gè)嚴(yán)重的問題。
● 程序集被同一個(gè)程序集的不同版本覆蓋——新版本與某些已有的客戶機(jī)代碼不兼容。
這些問題的解決方法是把共享程序集放在文件系統(tǒng)的一個(gè)特定的子目錄樹中,稱為全局程序集高速緩存(GAC)。與私有程序集不同,不能簡單地把共享程序集復(fù)制到對應(yīng)的文件夾中,而需要專門安裝到高速緩存中,這個(gè)過程可以用許多.NET工具來完成,其中包含對程序集的檢查、在程序集高速緩存中設(shè)置一個(gè)小的文件夾層次結(jié)構(gòu),以確保程序集的完整性。
為了避免名稱沖突,共享程序集應(yīng)根據(jù)私有密鑰加密法指定一個(gè)名稱(私有程序集只需要指定與其主文件名相同的名稱即可)。該名稱稱為強(qiáng)名(strong name),并保證其惟一性,它必須由要引用共享程序集的應(yīng)用程序來引用。
與覆蓋程序集相關(guān)的問題,可以通過在程序集清單中指定版本信息來解決,也可以通過同時(shí)安裝來解決。
1.4.3 反射
因?yàn)槌绦蚣鎯?chǔ)了元數(shù)據(jù),包括在程序集中定義的所有類型和這些類型的成員的細(xì)節(jié),所以可以編程訪問這些元數(shù)據(jù)。這個(gè)技術(shù)稱為反射,第11章詳細(xì)介紹了它們。該技術(shù)很有趣,因?yàn)樗硎就泄艽a實(shí)際上可以檢查其他托管代碼,甚至檢查它自己,以確定該代碼的信息。它們常常用于獲取特性的詳細(xì)信息,也可以把反射用于其他目的,例如作為實(shí)例化類或調(diào)用方法的一種間接方式,如果把方法上的類名指定為字符串,就可以選擇類來實(shí)例化方法,以便在運(yùn)行時(shí)調(diào)用,而不是在編譯時(shí)調(diào)用,例如根據(jù)用戶的輸入來調(diào)用(動(dòng)態(tài)綁定)。
|