十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊
量身定制 + 運營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
本次來討論一下基于.net平臺的CLR中的程序集加載的機(jī)制:
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:域名注冊、虛擬主機(jī)、營銷軟件、網(wǎng)站建設(shè)、楚雄州網(wǎng)站維護(hù)、網(wǎng)站推廣。
【注:由于.net已經(jīng)開源,可利用vs2015查看c#源碼的具體實現(xiàn)】
在運行時,JIT編譯器利用程序集的TypeRef和AssemblyRef元數(shù)據(jù)表來確定哪一個程序集定義了所引用的類型。在AssemblyRef元數(shù)據(jù)表的記錄項中,包含構(gòu)成程序集的強(qiáng)名稱的各個部分。JIT編譯器獲取包括名稱(無擴(kuò)展名和路徑)、版本、語言文化和公鑰標(biāo)記,將這些連接成一個字符串。JIT編譯器將該標(biāo)識匹配的一個程序集加載到AppDomain中。】
CLR內(nèi)部加載程序集提供了4中方法,在System.Refleetion.Assembly類中:
1. 采用靜態(tài)方法Load()加載程序集,可調(diào)用它顯示的將一個程序集加載到AppDomain中:
【注:Assembly類的Load()存在兩個重載版本】
////// 通過給定的程序集的顯示名稱來加載程序集,使用提供的證據(jù)將程序集加載到調(diào)用方的域中。 /// ////// 加載的程序集。 /// /// 程序集的顯示名稱。 用于加載程序集的證據(jù)。為 null。 未找到。 不是有效程序集。-或 -當(dāng)前加載的是 2.0 或更高版本的公共語言運行時,而 是用更高版本的公共語言運行時編譯的。 發(fā)現(xiàn)一個未能加載的文件。- 或 -用兩個不同的證據(jù)將一個程序集或模塊加載了兩次。 [SecuritySafeCritical] [Obsolete("This method is obsolete and will be removed in a future release of the .NET Framework. Please use an overload of Load which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly Load(string assemblyString, Evidence assemblySecurity) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, assemblySecurity, ref stackMark, false); } /// /// 通過給定程序集的長格式名稱加載程序集。 /// /// ////// 加載的程序集。 /// /// 程序集名稱的長格式。為 null。 是零長度字符串。 未找到。 發(fā)現(xiàn)一個未能加載的文件。 不是有效程序集。- 或 -當(dāng)前加載的是 2.0 或更高版本的公共語言運行時,而 是用更高版本的公共語言運行時編譯的。 [SecuritySafeCritical] [__DynamicallyInvokable] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly Load(string assemblyString) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, (Evidence) null, ref stackMark, false); }
在內(nèi)部,Load導(dǎo)致CLR向程序集應(yīng)用一個版本綁定重定向策略,并在GAC(全局程序集緩存)中查找程序集。如果沒有找到,就接著去應(yīng)用程序的基目錄、私有路徑目錄和codebase位置查找。如果Load找到指定的程序集,會返回對代表已加載的那個程序集的一個Assembly對象的引用。如果沒有找到,則會拋出一個異常。
【注:System.AppDomain提供了一個Load方法,這與Assembly的靜態(tài)Load方法不同,AppDoamin的Load是一個實例方法,它允許將一個程序集加載到一個指定的AppDoamin中,該方法設(shè)計供非托管代碼調(diào)用,語序宿主將一個程序集“注入”一個特定的AppDoamin。】
2.采用Assembly的LoadFrom方法,指定路徑名的方式加載程序集:
////// 已知程序集的文件名或路徑,加載程序集。 /// /// ////// 加載的程序集。 /// /// 包含程序集清單的文件的名稱或路徑。為 null。 未找到 ,或者嘗試加載的模塊沒有指定文件擴(kuò)展名。 發(fā)現(xiàn)一個未能加載的文件。 不是有效的程序集;例如,64 位進(jìn)程中的 32 位程序集。有關(guān)更多信息,請參見異常主題。- 或 -當(dāng)前加載的是 2.0 或更高版本的公共語言運行時,而 是用更高版本的公共語言運行時編譯的。 在沒有所需 的情況下,指定了不以“file://”開始的基本代碼。 參數(shù)是空字符串 ("")。 程序集名稱的長度大于 MAX_PATH 個字符。 [SecuritySafeCritical] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly LoadFrom(string assemblyFile) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoadFrom(assemblyFile, (Evidence) null, (byte[]) null, AssemblyHashAlgorithm.None, false, false, ref stackMark); }
(1).在內(nèi)部,LoadFrom首先會調(diào)用Syatem.Reflection.AssemblyName類的靜態(tài)方法GetAssemblyName。該方法打開指定的文件,查找AssemblyRef元數(shù)據(jù)表的記錄項,提取程序集標(biāo)識信息。
(2).以一個AssembleName對象的形式返回這些信息。
(3).LoadFrom方法內(nèi)部調(diào)用Assembly的Load方法,將Assembly對象傳遞給他。
(4).CLR會為應(yīng)用版本綁定重定向策略,并在各個位置查找匹配的程序集。
3.采用Assembly的LoadFile方法,這個方法可以從任意路徑加載一個程序集,并可將具有相同標(biāo)識的一個程序集多次加載到一個AppDoamin中。
////// 加載指定路徑上的程序集文件的內(nèi)容。 /// /// ////// 加載的程序集。 /// /// 要加載的文件的完全限定路徑。參數(shù)不是絕對路徑。 參數(shù)為 null。 發(fā)現(xiàn)一個未能加載的文件。 參數(shù)為空字符串 ("") 或不存在。 不是有效程序集。- 或 -當(dāng)前加載的是 2.0 或更高版本的公共語言運行時,而 是用更高版本的公共語言運行時編譯的。 [SecuritySafeCritical] public static Assembly LoadFile(string path) { AppDomain.CheckLoadFileSupported(); new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, path).Demand(); return (Assembly) RuntimeAssembly.nLoadFile(path, (Evidence) null); } /// /// 通過給定的程序集的路徑來加載程序集,使用提供的證據(jù)將程序集加載到調(diào)用方的域中。 /// /// ////// 加載的程序集。 /// /// 程序集文件的完全限定路徑。 用于加載程序集的證據(jù)。參數(shù)不是絕對路徑。 參數(shù)為 null。 參數(shù)為空字符串 ("") 或不存在。 發(fā)現(xiàn)一個未能加載的文件。 不是有效程序集。- 或 -當(dāng)前加載的是 2.0 或更高版本的公共語言運行時,而 是用更高版本的公共語言運行時編譯的。 不是 null。默認(rèn)情況下,舊的 CAS 策略中未啟用 .NET Framework 4; 如果未啟用), 必須是 null。 [SecuritySafeCritical] [Obsolete("This method is obsolete and will be removed in a future release of the .NET Framework. Please use an overload of LoadFile which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")] [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlEvidence)] public static Assembly LoadFile(string path, Evidence securityEvidence) { AppDomain.CheckLoadFileSupported(); if (securityEvidence != null && !AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled) throw new NotSupportedException(Environment.GetResourceString("NotSupported_RequiresCasPolicyImplicit")); new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, path).Demand(); return (Assembly) RuntimeAssembly.nLoadFile(path, securityEvidence); }
通過LoadFile加載程序集時,CLR不會自動解析任何依賴性問題,代碼必須向AppDomain的AssemblyReaolve事件登記,并讓事件回調(diào)方法顯示的加載依賴的程序集。
4.如果需要構(gòu)建的一個工具只是通過反射來分析程序集的元數(shù)據(jù),并希望確保程序集中的任何代碼都不會執(zhí)行,那么程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom方法或者使用ReflectionOnlyLoad方法。
////// 將給定顯示名稱的程序集加載到只反射上下文中。 /// /// ////// 加載的程序集。 /// /// 程序集的顯示名稱,由屬性返回。 為 null。 為空字符串 ("")。 未找到。 已找到,但是不能加載。 不是有效程序集。- 或 -當(dāng)前加載的是 2.0 或更高版本的公共語言運行時,而 是用更高版本的公共語言運行時編譯的。 [SecuritySafeCritical] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly ReflectionOnlyLoad(string assemblyString) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, (Evidence) null, ref stackMark, true); } /// /// 將給定路徑的程序集加載到只反射上下文中。 /// /// ////// 加載的程序集。 /// /// 包含程序集清單的文件的路徑。為 null。 未找到 ,或者嘗試加載的模塊沒有指定文件擴(kuò)展名。 已找到,但是未能加載。 不是有效程序集。- 或 -當(dāng)前加載的是 2.0 或更高版本的公共語言運行時,而 是用更高版本的公共語言運行時編譯的。 在沒有所需 的情況下,指定了不以“file://”開始的基本代碼。 程序集名稱的長度大于 MAX_PATH 個字符。 為空字符串 ("")。 [SecuritySafeCritical] [MethodImpl(MethodImplOptions.NoInlining)] public static Assembly ReflectionOnlyLoadFrom(string assemblyFile) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; return (Assembly) RuntimeAssembly.InternalLoadFrom(assemblyFile, (Evidence) null, (byte[]) null, AssemblyHashAlgorithm.None, true, false, ref stackMark); }
ReflectionOnlyLoadFrom方法加載有路徑指定的文件,文件的強(qiáng)名稱標(biāo)識不會獲取,也不會在GAC和其他位置搜索文件。ReflectionOnlyLoad方法會在GAC、應(yīng)用程序基目錄、私有路徑和codebase指定的位置搜索指定的程序集,該方法不會應(yīng)用版本控制策略,因此在指定的是那個版本,獲取的就是那個版本。如果要自行為一個程序集標(biāo)識指定版本控制策略,可將字符串傳給AppDoamin的ApplyPolicy方法。
用ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法加載程序集時,CLR禁止程序集中的任何代碼執(zhí)行,如果試圖執(zhí)行,則會拋出異常。