第一节:程序集加载
我們知道JIT編譯器將方法的IL代碼編譯成本地代碼時(shí),會(huì)查看IL代碼中引用了哪些類(lèi)型。在運(yùn)行時(shí),JIT編譯器利用程序集的TypeRef和AssemblyRef元數(shù)據(jù)表來(lái)確定哪一個(gè)程序集定義了所引用的類(lèi)型。在AssemblyRef元數(shù)據(jù)表的記錄項(xiàng)中,包含了構(gòu)成程序集強(qiáng)名稱(chēng)的各個(gè)部分。JIT編譯器獲取所有這些部分,包括名稱(chēng)(無(wú)擴(kuò)展名和路徑)、版本、語(yǔ)言文化和公鑰標(biāo)記,并把它連接成一個(gè)字符串。然后,JIT編譯器嘗試將與該標(biāo)識(shí)匹配的一個(gè)程序集加載到AppDomain中(如果還沒(méi)有加載的話)。如果被加載的程序集時(shí)弱命名的,那么標(biāo)識(shí)中只包含程序集的名稱(chēng)(不包含版本、語(yǔ)言文化以及公鑰標(biāo)記信息)。
在內(nèi)部,CLR使用System.Reflection.Assembly類(lèi)的靜態(tài)方法Load來(lái)嘗試加載這個(gè)程序集。這個(gè)方法在.NET Framework SDK文檔中是公開(kāi)的,可調(diào)用它顯式的將一個(gè)程序集加載到AppDomain中。這個(gè)方法是CLR中的與Win 32 LoadLibrary函數(shù)等價(jià)的方法。Assembly的Load方法實(shí)際上有幾個(gè)重載版本。以下是最常用的重載版本的原型:
public static Assembly Load(AssemblyName assemblyRef);
public static Assembly Load(string assemblyString);
在內(nèi)部,Load導(dǎo)致CLR向程序集應(yīng)用一個(gè)版本綁定重定向策略,并在GAC(全局程序集緩存)中查找程序集。如果沒(méi)找到,就接著去應(yīng)用程序的基目錄、私有路徑子目錄和codebase位置查找。如果調(diào)用Load時(shí),傳遞時(shí)一個(gè)弱命名程序集,Load就不會(huì)向程序集應(yīng)用一個(gè)版本綁定重定向策略,CLR也不會(huì)去GAC中查找程序集。如果Load找到程序集,會(huì)返回對(duì)代表已加載的那個(gè)程序集的一個(gè)Assembly對(duì)象的引用。如果Load沒(méi)有找到指定的程序集,會(huì)拋出一個(gè)System.IO.FileNotFoundException。
重要提示:一些開(kāi)發(fā)人員可能注意到,System.AppDomain提供了一個(gè)Load方法。和Assembly的Load方法不同,AppDomain的Load是一個(gè)實(shí)例方法,它允許將一個(gè)程序集加載到一個(gè)指定的AppDomain中。該方法設(shè)計(jì)供非托管代碼調(diào)用,允許宿主將一個(gè)程序集”注入”到一個(gè)AppDomain中。托管代碼的開(kāi)發(fā)人員一般情況下不應(yīng)調(diào)用它,因?yàn)樵谡{(diào)用AppDomain的Load方法時(shí),需要向它傳遞一個(gè)標(biāo)識(shí)了程序集的字符串,該方法隨后會(huì)應(yīng)用策略,并在一些常規(guī)位置搜索一些程序集。我們知道,AppDomain關(guān)聯(lián)了一些告訴CLR如何查找程序集的設(shè)置。為了加載這個(gè)程序集,CLR將使用與指定AppDomain關(guān)聯(lián)的設(shè)置,而不是與發(fā)出調(diào)用的那個(gè)AppDomain關(guān)聯(lián)的設(shè)置。
然后,AppDomain的Load方法會(huì)返回對(duì)程序集的一個(gè)引用System.Reflection.Assembly不是從MarshalByRefObject派生的,所以程序集對(duì)象必須按值封送回發(fā)出調(diào)用的那個(gè)AppDomain.但是,現(xiàn)在CLR就會(huì)用發(fā)出調(diào)用的那個(gè)AppDomain的設(shè)置來(lái)定位并加載程序集。如果使用發(fā)出調(diào)用的那個(gè)AppDomain的策略和搜索位置沒(méi)有找到指定的程序集,就會(huì)拋出一個(gè)FileNotFoundException。這個(gè)行為一般不是你所期望的,應(yīng)該避免使用AppDomain的Load方法。
在大多數(shù)動(dòng)態(tài)可擴(kuò)展的應(yīng)用程序中,Assembly的Load時(shí)程序集加載到AppDomain的首選方式。但是,它要求你實(shí)現(xiàn)掌握構(gòu)成程序集標(biāo)識(shí)的各個(gè)部分。開(kāi)發(fā)人員經(jīng)常要寫(xiě)一些工具和實(shí)用程序,以便在程序集上執(zhí)行一些處理。這些工具的例子包含:ILDasm.exe,PEVerify.exe,CorFlags.exe,GACUtil.exe, Sgen.exe,SN.exe,XSD.exe等,所有這些工具都要獲取引用一個(gè)程序集文件的路徑名(包括文件擴(kuò)展名)的命令行實(shí)參。為了以指定路徑名的的方式加載一個(gè)程序集,要調(diào)用Assembly的LoadForm方法。在內(nèi)部,LoadForm首先會(huì)調(diào)用System.Reflection.AssemblyName類(lèi)的靜態(tài)方法GetAssemblyName,該方法打開(kāi)指定的文件,查找AssemblyRef元數(shù)據(jù)表的記錄項(xiàng),提取程序集標(biāo)識(shí)信息,然后以一個(gè)System.Reflection.AssemblyName對(duì)象的形式返回這些信息(文件同時(shí)會(huì)關(guān)閉)。隨后,LoadForm方法在內(nèi)部調(diào)用Assembly的Load方法,將AssemblyName對(duì)象傳給他。然后,CLR會(huì)應(yīng)用版本綁定重定向策略,并在各個(gè)位置查找匹配的程序集,如果Load找到匹配的程序集,就會(huì)加載它,并返回代表已加載一個(gè)程序集的對(duì)象Assembly,LoadForm將返回這個(gè)值。如果Load沒(méi)有找到匹配的程序集,LoadForm就會(huì)加載通過(guò)LoadForm實(shí)參傳遞的路徑中的程序集。當(dāng)然,如果以加載了一個(gè)具有相同標(biāo)識(shí)的程序集,LoadFrom方法會(huì)簡(jiǎn)單的返回代表已加載程序集的一個(gè)Assembly對(duì)象。
VS的UI設(shè)計(jì)人員和其他工具一般用的是Assembly的LoadFile方法。這個(gè)方法可以從任何路徑加載一個(gè)程序集,并可將具有相同標(biāo)識(shí)的一個(gè)程序集多次加載到一個(gè)AppDomain中。在設(shè)計(jì)器/工具中對(duì)應(yīng)用程序的UI進(jìn)行了修改,CLR不會(huì)自動(dòng)解析任何依懶性問(wèn)題;你的代碼必須向AppDomain的AssemblyResolve事件登記,并讓事件回調(diào)方法顯示地加載任何依賴(lài)的程序集。
如果你構(gòu)建的一個(gè)工具只是通過(guò)反射來(lái)分析程序集的元數(shù)據(jù),并希望確保程序集中的任何代碼都不會(huì)執(zhí)行,那么加載程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom 或者ReflectionOnlyLoad方法。
ReflectionOnlyLoadFrom方法將加載由路徑指定的文件;文件的強(qiáng)名稱(chēng)標(biāo)識(shí)不會(huì)獲取,也不會(huì)在GAC和其他位置搜索。ReflectionOnlyLoad方法會(huì)在GAC、應(yīng)用程序基目錄、私有路徑和codebase指定的位置搜索指定的程序集。但是,和Load方法不同的是,ReflectionOnlyLoad不會(huì)應(yīng)用版本控制策略,所以你指定了哪個(gè)版本,獲得的就是哪個(gè)版本。如果要自行為一個(gè)程序集標(biāo)識(shí)指定版本控制策略,可以將字符串傳給AppDomain的ApplyPolicy方法。
ReflectionOnlyLoadFrom 或者ReflectionOnlyLoad方法加載程序集時(shí),CLR禁止程序集中的任何代碼執(zhí)行,試圖執(zhí)行由這兩個(gè)方法加載的程序集中的代碼,會(huì)導(dǎo)致CLR拋出一個(gè)異常,這兩個(gè)方法允許工具加載延遲簽名的程序集,這種程序集正常情況下會(huì)因?yàn)榘踩珯?quán)限不夠而無(wú)法加載。另外,這種程序集也可能是為不同的CPU架構(gòu)而創(chuàng)建的。
利用反射來(lái)分析由這兩個(gè)方法之一加載的程序集時(shí),代碼經(jīng)常需要向AppDomain的ReflectionOnlyAssemblyResovle事件注冊(cè)一個(gè)回調(diào)方法,以便手動(dòng)加載任何引用的程序集;CLR不會(huì)自動(dòng)幫你做這個(gè)事情,回調(diào)方法被調(diào)用時(shí),它必須調(diào)用Assembly的ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法,以便顯示加載一個(gè)程序集,并返回對(duì)程序集的一個(gè)引用。
許多應(yīng)用程序都由一個(gè)要依賴(lài)于眾多DLL文件的EXE文件組成。部署這個(gè)應(yīng)用程序時(shí),所有文件都必須部署。然后,有一個(gè)技術(shù)允許只部署一個(gè)EXE文件。首先,標(biāo)識(shí)出EXE文件要依賴(lài)的、同時(shí)不是作為.NET FRAMEWORK本身的一部分發(fā)布的所有DLL文件,將這些DLL添加到你的VS項(xiàng)目中,對(duì)于添加的這些DLL,有顯示它的屬性,并將它的“生成操作”更改為”嵌入的資源”。這回導(dǎo)致C#編譯器將DLL文件嵌入EXE文件中,以后只需部署這個(gè)EXE文件即可。
在運(yùn)行時(shí),CLR會(huì)找不到依賴(lài)的DLL程序集,為了解決這個(gè)問(wèn)題,應(yīng)用程序初始化時(shí),向AppDomain的ResolveAssembly事件登記一個(gè)回調(diào)方法,
?
?
AppDomain.CurrentDomain.AssemblyResolve += (sender, arg) =>{String resourceName = "AssemblyLoadingAndReflection:" + new AssemblyName(arg.Name) + ".dll";using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)){Byte[] assemblyData = new Byte[stream.Length];stream.Read(assemblyData, 0, assemblyData.Length);return Assembly.Load(assemblyData);}};
現(xiàn)在一個(gè)線程首次調(diào)用一個(gè)方法時(shí),如果發(fā)現(xiàn)該方法引用了依賴(lài)的DLL文件中的一個(gè)類(lèi)型,就會(huì)引發(fā)一個(gè)ResolveAssembly事件,而上訴回調(diào)代碼會(huì)找到所需的嵌入的DLL資源,并調(diào)用Assembly的Load方法重載版本,從而加載所需的資源。
?
轉(zhuǎn)載于:https://www.cnblogs.com/bingbinggui/p/4573487.html
總結(jié)
 
                            
                        - 上一篇: reset.css
- 下一篇: java 泛型的几点备忘
