在C#中如何獲取程序集
某一天我正在寫(xiě)一些反射代碼,目的是遍歷所有的程序集來(lái)查找一個(gè)特定的接口,然后在Startup中調(diào)用其上的一個(gè)方法??雌饋?lái)這個(gè)功能似乎很簡(jiǎn)單,但是在現(xiàn)實(shí)中,卻沒(méi)有一個(gè)清晰的,簡(jiǎn)單的,適合各種情形的方式來(lái)獲取一個(gè)程序集。這篇文章獲取對(duì)某些人來(lái)說(shuō)非常的枯燥,但是如果我能夠幫助哪怕一個(gè)人來(lái)解決此類(lèi)問(wèn)題,那么這篇文章也是值得的。
說(shuō)真的,由于有多種獲取程序集的方法,我將不會(huì)說(shuō)”使用這個(gè)方法”。很有可能,對(duì)于你的特定的工程來(lái)說(shuō),也許只有一種方式可以工作,所以依賴(lài)其他的方式是毫無(wú)意義的。讓我們簡(jiǎn)單的對(duì)所有的方式做個(gè)測(cè)試,然后看看哪一個(gè)方法是最合理的。
使用AppDomain.GetAssemblies
你可能會(huì)遇到的第一個(gè)選項(xiàng)是AppDomain.GetAssemblies。它(看似)加載了在AppDomain中的所有程序集,基本上可以說(shuō)加載了你項(xiàng)目使用到的每一個(gè)程序集。但是卻存在著大量的警告。在.NET中程序集是延遲加載到AppDomain中的,它不可能一次性加載所有的程序集,而是等你調(diào)用了一個(gè)程序集中的方法/類(lèi)的時(shí)候,它才會(huì)將它加載進(jìn)來(lái)--也就是即時(shí)加載。這是合理的,因?yàn)槿绻銖牟皇褂靡粋€(gè)程序集的話(huà),是沒(méi)有理由加載它的。
但問(wèn)題是在你調(diào)用AppDomain.GetAssemblies()的那個(gè)時(shí)間點(diǎn)上,如果你并沒(méi)有調(diào)用某個(gè)特定的程序集的方法,它便不會(huì)被加載?,F(xiàn)在如果你要為了Startup方法得到所有的程序集,很有可能你還沒(méi)有調(diào)用到那個(gè)程序集,這就意味著它還沒(méi)有加載到AppDomain,所以便獲取不到這個(gè)接口方法。
用代碼來(lái)說(shuō):
AppDomain.CurrentDomain.GetAssemblies(); // Does not return SomeAssembly as it hasn't been called yet. SomeAssembly.SomeClass.SomeMethod(); AppDomain.CurrentDomain.GetAssemblies(); // Will now return SomeAssembly.
雖然這看起來(lái)是一個(gè)很有吸引力的選項(xiàng),但是要知道,對(duì)于這個(gè)方法來(lái)說(shuō),時(shí)機(jī)是一切。
使用AssemblyLoad事件
因?yàn)槟悴荒艽_保當(dāng)你調(diào)用CurrentDomain.GetAssemblies()時(shí)所有程序集都被加載了,而實(shí)際上當(dāng)AppDomain加載另一個(gè)程序集的時(shí)候有一個(gè)事件會(huì)運(yùn)行?;旧险f(shuō),當(dāng)一個(gè)程序集被延遲加載的時(shí)候,你可以被通知到。它看起來(lái)像是這樣:
AppDomain.CurrentDomain.AssemblyLoad += (sender, args) =>
{
var assembly = args.LoadedAssembly;
};如果你只是想當(dāng)程序集加載的時(shí)候檢查下一些東西,那這或許是一個(gè)不錯(cuò)的解決方案,但是這個(gè)過(guò)程在某個(gè)特定的點(diǎn)并不是必然會(huì)發(fā)生(在你的.NET Core app的Startup.cs類(lèi)中并不會(huì)發(fā)生)。
這個(gè)方法的另一個(gè)問(wèn)題是到你添加你的事件處理器的那個(gè)時(shí)刻,并不能保證程序集還沒(méi)有被加載(事實(shí)上它們很可能已經(jīng)加載過(guò)了)。所以呢?你需要付出雙份的努力,首先添加你的事件處理器,之后迅速的檢查AppDomain.CurrentDomain.GetAssemblies,找到已經(jīng)被加載了的東西。
這是一個(gè)完美的解決方案,但是如果你習(xí)慣于使用延遲加載的程序集來(lái)做事的話(huà),這就不能正常工作了。
使用 GetReferencedAssemblies()
排名中的下一個(gè)是GetReferencedAssemblies()。本質(zhì)上你可以通過(guò)一個(gè)程序集,比如你的入口點(diǎn)程序集,一般來(lái)說(shuō)便是你的web項(xiàng)目,來(lái)得到所有引用的程序集。
其代碼本身看起來(lái)像是這樣:
Assembly.GetEntryAssembly().GetReferencedAssemblies();
再一次,看起來(lái)像是在玩把戲,但是這個(gè)方法有另一個(gè)很大的問(wèn)題。在許多項(xiàng)目中會(huì)有一個(gè)“模式分離”的概念,比如 Web Project>>Service Project>>Data Project。Web Project本身并不直接引用Data Project。而當(dāng)你調(diào)用“GetReferencedAssemblies”時(shí)其意味著直接引用。因此如果你期望在程序集列表中得到Data Project,你將會(huì)失望不已。
所以,再一次的,在一些情況下可以正常工作,但并不是一個(gè)普遍的解決方案。
循環(huán)GetReferencedAssemblies()
使用GetReferencedAssemblies()的另一個(gè)選擇是創(chuàng)建一個(gè)方法來(lái)遍歷所有的程序集。類(lèi)似于這樣:
public static List GetAssemblies()
{
var returnAssemblies = new List();
var loadedAssemblies = new HashSet();
var assembliesToCheck = new Queue();
assembliesToCheck.Enqueue(Assembly.GetEntryAssembly());
while(assembliesToCheck.Any())
{
var assemblyToCheck = assembliesToCheck.Dequeue();
foreach(var reference in assemblyToCheck.GetReferencedAssemblies())
{
if(!loadedAssemblies.Contains(reference.FullName))
{
var assembly = Assembly.Load(reference);
assembliesToCheck.Enqueue(assembly);
loadedAssemblies.Add(reference.FullName);
returnAssemblies.Add(assembly);
}
}
}
return returnAssemblies;
}這個(gè)方法的邊界處理得有點(diǎn)粗糙,但是它的確可以工作并且意味著在Startup方法中,你可以立即看到所有的程序集。
關(guān)于這個(gè)方法你可能會(huì)被卡住的一次是如果你正在動(dòng)態(tài)加載程序集,并且它們實(shí)際上并不會(huì)被任何項(xiàng)目所引用。對(duì)于這種情況,你需要下一個(gè)方法。
目錄DLL加載
一個(gè)很粗糙的獲取所有解決方案dll的方式是將它們加載出你的bin文件夾??雌饋?lái)像是這樣:
public static Assembly[] GetSolutionAssemblies()
{
var assemblies = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
.Select(x => Assembly.Load(AssemblyName.GetAssemblyName(x)));
return assemblies.ToArray();
}它可以正常工作但的確是一個(gè)粗糙的解決方案。但是使用這個(gè)方式的一個(gè)最大的好處是一個(gè)dll只需要簡(jiǎn)單的放置在需要加載的目錄中就可以。因此如果你出于任何原因動(dòng)態(tài)的加載DLLs,對(duì)于你來(lái)說(shuō),這很可能是唯一的方法(除過(guò)在AppDomain中監(jiān)聽(tīng)AssemblyLoad)。
這是做這件事情的看起來(lái)像惡作劇的方式之一。但是很可能你已經(jīng)被阻擋到角落之中而這正是解決問(wèn)題的唯一方式。
僅僅得到“我的”程序集
使用這些方法中的任何一種,您會(huì)很快發(fā)現(xiàn)你正在將Nuget下的每個(gè)程序集加載到你的項(xiàng)目中,包括Nuget包、.NET核心庫(kù)甚至運(yùn)行時(shí)特定的dll。在.NET世界中,程序集就是程序集。沒(méi)有“是的,但這是我的程序集”并且它們很特別的概念。
過(guò)濾的唯一方法就是檢查名字。您可以將其作為白名單來(lái)執(zhí)行,因此如果解決方案中的所有項(xiàng)目都以“MySolution”開(kāi)頭。因而你可以像這樣來(lái)進(jìn)行過(guò)濾:
Assembly.GetEntryAssembly().GetReferencedAssemblies().Where(x => x.Name.StartsWith("MySolution."))或者你可以選擇一個(gè)黑名單選項(xiàng),這個(gè)選項(xiàng)并不真正限制你的程序集,但至少可以減少你正在加載/檢查/處理的程序集的數(shù)量。就像這樣:
Assembly.GetEntryAssembly().GetReferencedAssemblies()
.Where(x => !x.Name.StartsWith("Microsoft.") && !x.Name.StartsWith("System."))黑名單可能看起來(lái)很蠢,但在某些情況下,如果您正在構(gòu)建一個(gè)實(shí)際上不知道最終解決方案名稱(chēng)的庫(kù),那么這是減少您試圖加載的內(nèi)容的唯一方法。
到此這篇關(guān)于在C#中如何獲取程序集的文章就介紹到這了,更多相關(guān)C#獲取程序集內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#數(shù)據(jù)結(jié)構(gòu)之循環(huán)鏈表的實(shí)例代碼
C#數(shù)據(jù)結(jié)構(gòu)之循環(huán)鏈表的實(shí)例代碼,需要的朋友可以參考一下2013-03-03
C#實(shí)現(xiàn)協(xié)同過(guò)濾算法的實(shí)例代碼
這篇文章介紹了C#實(shí)現(xiàn)協(xié)同過(guò)濾算法的實(shí)例代碼,有需要的朋友可以參考一下2013-07-07
unity實(shí)現(xiàn)場(chǎng)景跳轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了unity實(shí)現(xiàn)場(chǎng)景跳轉(zhuǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
C#實(shí)現(xiàn)文本文件讀寫(xiě)方法匯總
本文給大家匯總介紹了C#實(shí)現(xiàn)文本文件讀寫(xiě)的方法,十分的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下。2015-06-06
WMI獲取硬件信息封裝函數(shù)方法(聯(lián)想臺(tái)式機(jī)出廠編號(hào) CPUID BIOS序列號(hào) 硬盤(pán)信息 顯卡信息 MAC地址)
這篇文章主要介紹了WMI獲取硬件信息的方法,硬件信息有:聯(lián)想臺(tái)式機(jī)出廠編號(hào) CPUID BIOS序列號(hào) 硬盤(pán)信息 顯卡信息 MAC地址2013-11-11

