欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#中應(yīng)用程序集的裝載過程詳解

 更新時(shí)間:2020年12月08日 09:35:26   作者:michaelscode  
這篇文章主要介紹了C#中應(yīng)用程序集的裝載過程的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

了解程序集如何在C#.NET中加載

我們一直在處理庫和NuGet軟件包。不管是好是壞,高級.NET開發(fā)人員都需要了解.NET運(yùn)行時(shí)如何加載程序集。

這些庫依賴于其他流行的庫,并且有很多共享的依賴項(xiàng)。有了足夠大的依賴關(guān)系網(wǎng)絡(luò),您最終將陷入沖突或困境。處理此類問題的最佳方法是了解該機(jī)制在內(nèi)部的工作方式。

在本文中,您將看到.NET進(jìn)程如何以及何時(shí)加載引用的程序集。

您將了解加載了哪個(gè)庫版本,當(dāng)有多個(gè)可用版本時(shí)會發(fā)生什么,以及為什么有時(shí)由于版本沖突而出現(xiàn)問題。

您將看到如何調(diào)試這些類型的問題,查看程序集綁定日志(融合日志)以及一些解決沖突的方法。

程序集,模塊和引用

讓我們從圍繞.NET流程的一些基本術(shù)語開始。

一個(gè)裝配在.NET是一個(gè)DLL或EXE文件。Visual Studio解決方案中的每個(gè)項(xiàng)目都被編譯為一個(gè)程序集。

每個(gè)程序集可以包含多個(gè)模塊,但是實(shí)際上,我們幾乎總是在一個(gè)程序集中有一個(gè)模塊,該模塊的名稱與該程序集相同。

在Visual Studio中啟動進(jìn)程或單擊F5時(shí),將執(zhí)行啟動項(xiàng)目程序集。除了.NET Framework或.NET Core程序集之外,它將是第一個(gè)加載的程序集。

之后,該過程將根據(jù)需要在運(yùn)行時(shí)加載其他程序集。僅當(dāng)需要調(diào)用該程序集的方法或使用該程序集的類型時(shí),它才會延遲加載程序集。

這里是為一個(gè)簡單的“ Hello World” .NET Framework項(xiàng)目加載的模塊(出于我們所有的意圖和目的,模塊和程序集都是相同的)。MyStartup.dll是此處的啟動項(xiàng)目:

.NET Core項(xiàng)目啟動時(shí)加載的模塊

當(dāng)您從另一個(gè)項(xiàng)目引用一個(gè)項(xiàng)目時(shí),在構(gòu)建時(shí),被引用項(xiàng)目的DLL或EXE被復(fù)制到啟動項(xiàng)目的Bin文件夾中。

通常是Bin \ Debug或Bin \ Release。在運(yùn)行時(shí),當(dāng)您第一次使用引用的項(xiàng)目中的類型時(shí),CLR在應(yīng)用程序目錄中查找具有與期望的名稱和版本相同的DLL文件。然后將程序集加載到流程中。這也稱為綁定到裝配件。

這是一個(gè)例子:

假設(shè)我們有一個(gè)名為MyStartup的簡單控制臺應(yīng)用程序,它引用了另一個(gè)名為Lib1的項(xiàng)目。MyStartup使用Lib1程序集中的某些類。

在MyStartup中:

class Program
{
 static void Main(string[] args)
 {
 int a = int.Parse(Console.ReadLine());
 int b = int.Parse(Console.ReadLine());
 Console.WriteLine("A + B = " + Add(a, b));
 }

 private static int Add(int a, int b)
 {
 var calculator = new Lib1.Calculator();
 return calculator.Sum(a, b);
 }
}

在Lib1中:

public class Calculator
{
 public int Sum(int a, int b)
 {
 return a + b;
 }
}

輸入Main方法時(shí),尚未加載Lib1程序集。但是,在輸入Add方法時(shí),CLR嘗試解析Calculator類型,找出它在引用的程序集Lib1中,然后嘗試加載該程序集。

.NET中的程序集綁定

當(dāng)CLR需要加載程序集時(shí),邏輯實(shí)際上比在Bin文件夾中查找要復(fù)雜一些。這是執(zhí)行的實(shí)際邏輯(有關(guān)詳細(xì)說明,請參見Microsoft文檔[1]):

1.根據(jù)配置文件(app.config或web.config)確定需要加載的程序集的版本。該配置文件的名稱為(在生成之后) [executable name].exe.config或web.config。綁定重定向在這里發(fā)揮了作用(稍后會詳細(xì)介紹)。

2.查看程序集是否已加載。如果加載了其他版本,則將拋出FileLoadException,除非它是一個(gè)可以同時(shí)加載多個(gè)版本的強(qiáng)命名程序集。

3.如果它是強(qiáng)名稱程序集,請檢查全局程序集緩存[2](GAC)。GAC是機(jī)器上共享多個(gè)應(yīng)用程序部件的地方。如果需要的話,程序集會緩存。它只能存儲強(qiáng)命名程序集。它可以存儲同一程序集的不同版本。您可以使用gacutil.exe[3]自己將其安裝到GAC 。

4.如果它是一個(gè)強(qiáng)名稱的程序集,并且配置文件包含<codeBase>節(jié)點(diǎn),那么它將檢查那里的程序集位置。如果該<codeBase>節(jié)點(diǎn)存在并且找不到程序集,F(xiàn)ileNotFoundException則將引發(fā)a。

5.根據(jù)啟發(fā)式算法檢查程序集DLL或EXE。此過程稱為“探測”。算法如下:

1.檢查文件夾[application base] / [assembly name].dll。應(yīng)用程序庫是應(yīng)用程序可執(zhí)行文件所在的位置。通常,您的Bin \ Debug或Bin \ Release文件夾。
2.檢查一下 [application base] / [assembly name] / [assembly name].dll
3.如果為引用的程序集指定了區(qū)域性信息,則僅檢查以下目錄: [application base] / [culture] / [assembly name].dll [application base] / [culture] / [assembly name] / [assembly name].dll
4.如果該<probing>節(jié)點(diǎn)存在于配置文件中,則它將在該privatePath節(jié)點(diǎn)的屬性指定的文件夾中查找程序集。

他們?yōu)槭裁匆顾惺虑樽兊萌绱死щy,對嗎?

實(shí)際上,這種邏輯非常有助于我們發(fā)展,而不會使事情變得困難。它的存在是為了實(shí)現(xiàn)一些重要目標(biāo):

•為了確保您引用的是特定的程序集和版本,則將加載該確切版本。否則,將引發(fā)異常。而且,如果您知道自己在做什么,則可以在配置文件中指定覆蓋規(guī)則(綁定重定向)。

•為了靈活地在您要加載的程序集中進(jìn)行。例如,如果要根據(jù)不同的區(qū)域性(語言)加載不同的程序集,則可以輕松地做到這一點(diǎn)?;蛘?,如果您要根據(jù)客戶配置加載不同的程序集,那也可以。

•為了安全起見,我們使用了全稱的程序集。他們確保您不能“偽造”程序集。例如,如果某個(gè)進(jìn)程希望加載Lib1 v4.5,那么您將無法加載具有相同名稱和版本的惡意軟件程序集。加載時(shí)會引發(fā)異常。這就是為什么在計(jì)算機(jī)上所有進(jìn)程都共享的GAC只接受強(qiáng)名稱程序集的原因。

在大多數(shù)應(yīng)用程序中,您無需記住程序集加載和探測的復(fù)雜邏輯。您無需了解或考慮GAC,全名程序集或操作配置文件。

您幾乎根本不需要考慮庫的版本,因?yàn)榭赡艿臎_突通過稱為“綁定重定向”的機(jī)制自動解決了。

綁定重定向

如果有一件事對于了解這筆交易非常重要,那就是綁定重定向。能夠告訴運(yùn)行時(shí)它將實(shí)際加載哪個(gè)版本,而不管其引用的版本如何。

這是一個(gè)示例:您的流程有兩個(gè)項(xiàng)目(模塊):項(xiàng)目A和項(xiàng)目B。項(xiàng)目A引用log4net.dll v1.1,項(xiàng)目B引用log4net.dll v1.2。兩個(gè)log4net DLL文件都復(fù)制到輸出文件夾,但是只能有一個(gè)log4net.dll文件。

假設(shè)復(fù)制到輸出文件夾的文件是log4net.dll v1.2。假設(shè)到達(dá)的第一個(gè)代碼是Project A中的代碼,該代碼引用了log4net v1.1。運(yùn)行時(shí)將在輸出文件夾中查找,找到不同版本的log4net,并失敗FileLoadException。

還有另一種可能。假設(shè)首先執(zhí)行了項(xiàng)目B中的代碼,并且在嘗試使用log4net時(shí),它成功加載了log4net.dll v1.2。片刻之后,Project A中的代碼將嘗試使用log4net v1.1,請參見該程序集已經(jīng)加載了其他版本,并拋出FileLoadException。

如果您知道哪個(gè)log4net版本將在輸出文件夾中,在這種情況下可以做的就是告訴運(yùn)行時(shí)應(yīng)該使用哪個(gè)版本。只需app.config在該runtime部分的文件中添加以下幾行:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 ...
 <runtime>
 ...
 <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
 <dependentAssembly>
 <assemblyIdentity name="log4net" 
    publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
 <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="1.2.0" />
 </dependentAssembly>
 </assemblyBinding>
 </runtime>
 ...
</configuration>

這意味著,只要運(yùn)行時(shí)想綁定到版本范圍為0.0.0.0to的程序集log4net 5.0.0.0,它就會嘗試綁定到version 1.2.0。

實(shí)際上,您不必手動添加這些重定向,因?yàn)樗鼈兪亲詣犹砑拥?。如果轉(zhuǎn)到啟動項(xiàng)目的“屬性”,則會看到以下設(shè)置:

默認(rèn)情況下選中此選項(xiàng)。它會自動檢測版本沖突并在.config文件中生成綁定重定向。

當(dāng)問題開始發(fā)生時(shí)

乍一看,綁定重定向可能看起來像是對所有問題的答案,但事實(shí)并非如此。使用綁定重定向時(shí),基本上使用的庫版本與預(yù)期不同。如果刪除方法怎么辦?或方法的簽名已更改?在這種情況下,調(diào)用該方法時(shí),程序?qū)⒁蜻\(yùn)行時(shí)錯(cuò)誤而失敗。畢竟,創(chuàng)建版本是有原因的。

如果確實(shí)存在此類問題,則有解決方法。查看我的文章:如何解決.NET引用和NuGet軟件包版本沖突[4]。

故障排除

當(dāng)您有一個(gè)FileLoadException或類似的東西時(shí),我建議做的第一件事是查看Visual Studio中的“模塊”窗口。在這里,您將看到所有已加載的模塊,并確定您要加載的程序集是否已加載,使用哪個(gè)版本以及從哪個(gè)路徑加載。

除此之外,您還可以查看程序集綁定日志,也稱為融合日志。這些日志將顯示在程序集綁定嘗試過程中到底發(fā)生了什么。您將看到運(yùn)行時(shí)查找的程序集版本,運(yùn)行時(shí)查找的文件夾以及故障點(diǎn)。

有幾種查看融合日志的方法。首先,您必須啟用它們,因?yàn)槟J(rèn)情況下它們是禁用的。您可以通過將HKLM\Software\Microsoft\Fusion\ForceLog值設(shè)置為1并將HKLM\Software\Microsoft\Fusion\LogPath值設(shè)置為來在注冊表中手動啟用它們C:\FusionLogs。日志將自動出現(xiàn)?;蛘?,您可以使用Fusion Log Viewer,該軟件應(yīng)以方式安裝在PC上fuslogvw.exe。我建議使用“一切窗口”搜索之[5]類的程序來查找它。確保以管理員權(quán)限運(yùn)行融合日志查看器,以便能夠啟用和禁用日志。最近更流行的一種更現(xiàn)代的工具是Fusion ++[6]。

邊注

也許您不需要,但是我以前討厭不得不處理這類問題。例如一個(gè)邏輯上的問題,讓我構(gòu)建一些東西,甚至解決一個(gè)生產(chǎn)錯(cuò)誤,但其他問題都好說,唯獨(dú)這個(gè)……。

在這件事上別無選擇,我不得不艱難地學(xué)習(xí)程序集綁定的內(nèi)部工作。我發(fā)現(xiàn),就像其他所有內(nèi)容一樣,一旦您理解了某些內(nèi)容,它就會變得不那么可怕,甚至變得不再那么有趣了。

因此,我希望本文對您有意義,并會在我走過的道路上為您提供快速幫助。

References

[1] Microsoft文檔: https://docs.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies

[2] 全局程序集緩存: https://docs.microsoft.com/en-us/dotnet/framework/app-domains/gac

[3] gacutil.exe: https://docs.microsoft.com/en-us/dotnet/framework/tools/gacutil-exe-gac-tool

[4] 如何解決.NET引用和NuGet軟件包版本沖突: https://michaelscodingspot.com/how-to-resolve-net-reference-and-nuget-package-version-conflicts/

[5] 一切窗口”搜索之: https://www.voidtools.com/

[6] Fusion ++: https://github.com/awaescher/Fusion/

到此這篇關(guān)于C#中應(yīng)用程序集的裝載過程詳解的文章就介紹到這了,更多相關(guān)C#應(yīng)用程序集的裝載過程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論