C#反射之Assembly.Load,Assembly.LoadFile 与 Assembly.LoadFrom方法介绍
一些關(guān)于C#反射的知識(shí),估計(jì)也就最多達(dá)到使用API的程度,至于要深入了解,以現(xiàn)在的水平估計(jì)很難做到,所以下面此篇文章,以作為一個(gè)階段的總結(jié)。
對(duì)于反射的總結(jié),我想從以下幾個(gè)方面展開,首先是反射程序集,模塊,類的成員以及成員的一些信息;接下來(lái)就是動(dòng)態(tài)調(diào)用類的成員方法;第三個(gè)方面就動(dòng)態(tài)產(chǎn)生程序集,模塊和類以及類的成員。好了,現(xiàn)在就讓我們從反射各種信息開始吧
在C#中,我們要使用反射,首先要搞清楚以下命名空間中幾個(gè)類的關(guān)系:
System.Reflection命名空間
(1)?? AppDomain:應(yīng)用程序域,可以將其理解為一組程序集的邏輯容器
(2)?? Assembly:程序集類
(3)?? Module:模塊類
(4)?? Type:使用反射得到類型信息的最核心的類
他們之間是一種從屬關(guān)系,也就是說(shuō),一個(gè)AppDomain可以包含N個(gè)Assembly,一個(gè)Assembly可以包含N個(gè)Module,而一個(gè)Module可以包含N個(gè)Type.
AppDomain這個(gè)類我們等下再來(lái)講解。我們先關(guān)注Assembly個(gè)類
在程序中,如果我們要?jiǎng)討B(tài)加載一個(gè)程序集怎么辦呢?有幾種方式可以使用,分別是Load、LoadFrom和LoadWithPartialName三個(gè)Assembly的靜態(tài)方法.
先來(lái)講解Assembly.Load方法,該方法會(huì)有多個(gè)重載版本,其中一個(gè)就是提供程序集的詳細(xì)信息,即程序集的標(biāo)識(shí),包括程序集的名稱,版本,區(qū)域信息,公有密鑰標(biāo)記,全部都是以一個(gè)字符串的形式提供,例如:"MyAssembly,Version=1.0.0.0,culture=zh-CN,PublicKeyToken=47887f89771bc57f”.
那么,使用Assembly.Load加載程序集的順序是怎樣的呢?首先它會(huì)去全局程序集緩存查找,然后到應(yīng)用程序的根目錄查找,最后會(huì)到應(yīng)用程序的私有路徑查找。
當(dāng)然,如果你使用的是弱命名程序集,也即只給出程序集的名稱,那么這個(gè)時(shí)候,CLR將不會(huì)在程序集上應(yīng)用任何安全或者部署策略,而且Load也不會(huì)到全局緩存程序集中查找程序集。
測(cè)試加載弱命名程序集的例子如下:
(1)?? 新建一個(gè)控制臺(tái)應(yīng)用程序的工程,同時(shí)勾選創(chuàng)建解決方案
(2)?? 在解決方案中新建一個(gè)類庫(kù)的項(xiàng)目,隨便寫一個(gè)類和一個(gè)方法
(3)?? 在控制臺(tái)項(xiàng)目中,首先不添加引用,直接在Main方法中添加如下代碼:
Assembly assembly = Assembly.Load("MyAssembly");
if (assembly != null)
{ Console.WriteLine("加載成功"); }
執(zhí)行程序,會(huì)拋出異常,說(shuō)找不到該程序集。什么原因呢?因?yàn)槲覀兪褂玫氖侨趺绦蚣?#xff0c;Load方法不會(huì)去全局程序集緩存中查找,而該應(yīng)用程序目錄下又沒(méi)有該程序集,所以程序找不到。這個(gè)時(shí)候,我們把程序稍微改一下,不用添加代碼,只需添加對(duì)MyAssembly的引用,重新運(yùn)行程序,加載成功了。
接下來(lái),我們就要看看Load怎么加載強(qiáng)命名程序集了,這個(gè)步驟稍微有些復(fù)雜。還是剛才的項(xiàng)目,找到MyAssembly.dll程序集所在的目錄,一般在bin"Debug目錄下
(1)生成密鑰對(duì)文件?? sn –k MyAssemblyKey.keys
你也可以自己隨便起一個(gè)密鑰對(duì)文件名
(2)生成公鑰文件
sn –p MyAssemblyKey.keys MyAssemblyPublicKey.PublicKey
注:查看公鑰命令:sn –tp MyAssemblyPublicKey.PublicKey
(3)創(chuàng)建強(qiáng)命名程序集。
很簡(jiǎn)單,只需要在聲明命名空間的那句代碼上加上如下特性:
[assembly:AssemblyKeyFileAttribute(@”D:"Test"MyAssemblyKey.keys”)]
(4)?? 編譯項(xiàng)目
(5)?? 將程序集添加到程序集全局緩存
gacutil –i MyAssembly.dll
這個(gè)時(shí)候,轉(zhuǎn)到加載程序集的項(xiàng)目中,將Load方法中的參數(shù)改為”程序集名,Version=版本,culture=區(qū)域信息,PublicKeyToken=公鑰“,然后再去掉對(duì)程序集的引用,我們會(huì)發(fā)現(xiàn),程序運(yùn)行成功。表明Load到全局緩存區(qū)查找到了該程序集。
使用Load方法加載程序集,特別是強(qiáng)命名程序集,能在程序集上應(yīng)用安全和部署策略,推薦使用該方法動(dòng)態(tài)加載程序集,至于LoadFrom和LoadWithPartialName。
首先我們還是來(lái)看看LoadFrom方法,這個(gè)方法的原理是這樣的:我們?nèi)绻褂盟鼇?lái)動(dòng)態(tài)加載程序集,必須告訴它程序集的路徑,也即在哪個(gè)目錄下面,CLR會(huì)去加載與你指定的路徑完全匹配的程序集。記住,當(dāng)我們指定程序集路徑時(shí),不能包括任何關(guān)于程序集強(qiáng)命名的信息,所以,CLR不會(huì)在我們指定的程序集文件上應(yīng)用任何策略,而且也不會(huì)去任何其他的地方搜索程序集,簡(jiǎn)言之,它就是指哪打哪,呵呵。
例如:你有個(gè)程序集在D:/Test/MyAssembly.dll,你要用Assembly.LoadFrom加載該程序集,代碼就如下:
Assembly assembly = Assembly.LoadFrom(@”D:/Test/MyAssembly.dll”);
對(duì)于,LoadWithParitalName方法,推薦大家最好不要使用它,因?yàn)槌绦驘o(wú)法確定最終要去加載哪個(gè)程序集的版本,所以我們這里只是簡(jiǎn)單的介紹一下它的工作原理:你可以傳遞一個(gè)程序集標(biāo)識(shí)給它,包括程序集名稱,至于其他信息是可選的(區(qū)域信息,公有密鑰等),該方法執(zhí)行時(shí),會(huì)首先檢查應(yīng)用程序中配置文件的qualifyAssembly節(jié)點(diǎn),如果存在,則把該部分名稱的程序集替換成完全的程序集標(biāo)識(shí),如果不存在,則使用程序集名稱先到應(yīng)用程序根目錄下查找,然后是私有目錄,沒(méi)有找到的話,就到程序集全局緩存中查找。簡(jiǎn)單過(guò)程如下:
?????? 應(yīng)用程序根目錄 -> 應(yīng)用程序私有目錄 -> 程序集全局緩存.
Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的區(qū)別!
1,Assembly.Load()
這個(gè)方法通過(guò)程序集的長(zhǎng)名稱(包括程序集名,版本信息,語(yǔ)言文化,公鑰標(biāo)記)來(lái)加載程序集的,會(huì)加載此程序集引用的其他程序集,一般情況下都應(yīng)該優(yōu)先使用 這個(gè)方法,他的執(zhí)行效率比LoadFrom要高很多,而且不會(huì)造成重復(fù)加載的問(wèn)題(原因在第2點(diǎn)上說(shuō)明)
使用這個(gè)方法的時(shí)候, CLR會(huì)應(yīng)用一定的策略來(lái)查找程序集,實(shí)際上CLR按如下的順序來(lái)定位程序集:
⑴如果程序集有強(qiáng)名稱,在首先在全局程序集緩(GAC)中查找程序集。?????????
⑵如果程序集的強(qiáng)名稱沒(méi)有正確指定或GAC中找不到,那么通過(guò)配置文件中的<codebase>元素指定的URL來(lái)查找
⑶如果沒(méi)有指定強(qiáng)名稱或是在GAC中找不到,CLR會(huì)探測(cè)特定的文件夾:
假設(shè)你的應(yīng)用程序目錄是C:/AppDir,<probing>元素中的privatePath指定了一個(gè)路徑Path1,你要定位的程序集是AssemblyName.dll則CLR將按照如下順序定位程序集
????????? C:/AppDir/AssemblyName.dll
????????? C:/AppDir/AssemblyName/AssemblyName.dll
????????? C:/AppDir/Path1/AssemblyName.dll
????????? C:/AppDir/Path1/AssemblyName/AssemblyName.dll
如果以上方法不能找到程序集,會(huì)發(fā)生編譯錯(cuò)誤,如果是動(dòng)態(tài)加載程序集,會(huì)在運(yùn)行時(shí)拋出異常!
2,Assembly.LoadFrom()
這個(gè)方法從指定的路徑來(lái)加載程序集,實(shí)際上這個(gè)方法被調(diào)用的時(shí)候,CLR會(huì)打開這個(gè)文件,獲取其中的程序集版本,語(yǔ)言文化,公鑰標(biāo)記等信息,把他們傳遞給 Load方法,接著,Load方法采用上面的策略來(lái)查找程序集。如果找到了程序集,會(huì)和LoadFrom方法中指定的路徑做比較,如果路徑相同,該程序集 會(huì)被認(rèn)為是應(yīng)用程序的一部分,如果路徑不同或Load方法沒(méi)有找到程序集,那該程序集只是被作為一個(gè)“數(shù)據(jù)文件”來(lái)加載,不會(huì)被認(rèn)為是應(yīng)用程序的一部分。 這就是在第1點(diǎn)中提到的Load方法比LoadFrom方法的執(zhí)行效率高的原因。另外,由于可能把程序集作為“數(shù)據(jù)文件”來(lái)加載,所以使用 LoadFrom從不同路徑加載相同程序集的時(shí)候會(huì)導(dǎo)致重復(fù)加載。當(dāng)然這個(gè)方法會(huì)加載此程序集引用的其他程序集。
3,Assembly.LoadFile()
這個(gè)方法是從指定的文件來(lái)加載程序集,和上面方法的不同之處是這個(gè)方法不會(huì)加載此程序集引用的其他程序集!
結(jié)論:一般大家應(yīng)該優(yōu)先選擇Load方法來(lái)加載程序集,如果遇到需要使用LoadFrom方法的時(shí)候,最好改變?cè)O(shè)計(jì)而用Load方法來(lái)代替!
另:Assembly.LoadFile 與 Assembly.LoadFrom的區(qū)別
1、Assembly.LoadFile只載入相應(yīng)的dll文件,比如Assembly.LoadFile("abc.dll"),則載入abc.dll,假如abc.dll中引用了def.dll的話,def.dll并不會(huì)被載入。
Assembly.LoadFrom則不一樣,它會(huì)載入dll文件及其引用的其他dll,比如上面的例子,def.dll也會(huì)被載入。
2、用Assembly.LoadFrom載入一個(gè)Assembly時(shí),會(huì)先檢查前面是否已經(jīng)載入過(guò)相同名字的Assembly,比如abc.dll有兩個(gè)版本(版本1在目錄1下,版本2放在目錄2下),程序一開始時(shí)載入了版本1,當(dāng)使用Assembly.LoadFrom("2//abc.dll")載入版本2時(shí),不能載入,而是返回版本1。Assembly.LoadFile的話則不會(huì)做這樣的檢查,比如上面的例子換成Assembly.LoadFile的話,則能正確載入版本2。
LoadFile:加載指定路徑上的程序集文件的內(nèi)容。LoadFrom: 根據(jù)程序集的文件名加載程序集文件的內(nèi)容。
區(qū)別:
LoadFile 方法用來(lái)來(lái)加載和檢查具有相同標(biāo)識(shí)但位于不同路徑中的程序集.但不會(huì)加載程序的依賴項(xiàng)。
LoadFrom 不能用于加載標(biāo)識(shí)相同但路徑不同的程序集。
?
轉(zhuǎn)自:http://blog.csdn.net/guxiaoshi/article/details/5009604
轉(zhuǎn)載于:https://www.cnblogs.com/vic-zhang/p/3273526.html
總結(jié)
以上是生活随笔為你收集整理的C#反射之Assembly.Load,Assembly.LoadFile 与 Assembly.LoadFrom方法介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 并发编程(多进程1)
- 下一篇: c# char unsigned_dll