<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文看懂C#中應(yīng)用程序集的裝載過(guò)程

          共 5882字,需瀏覽 12分鐘

           ·

          2020-12-28 01:08

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

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

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

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

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

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

          程序集,模塊和引用

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

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

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

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

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

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

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


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

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

          這是一個(gè)例子:

          假設(shè)我們有一個(gè)名為MyStartup的簡(jiǎn)單控制臺(tái)應(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ì)說(shuō)明,請(qǐng)參見(jiàn)Microsoft文檔[1]):

          1.根據(jù)配置文件(app.configweb.config)確定需要加載的程序集的版本。該配置文件的名稱為(在生成之后) [executable name].exe.configweb.config。綁定重定向在這里發(fā)揮了作用(稍后會(huì)詳細(xì)介紹)。2.查看程序集是否已加載。如果加載了其他版本,則將拋出FileLoadException,除非它是一個(gè)可以同時(shí)加載多個(gè)版本的強(qiáng)命名程序集。3.如果它是強(qiáng)名稱程序集,請(qǐng)檢查全局程序集緩存[2](GAC)。GAC是機(jī)器上共享多個(gè)應(yīng)用程序部件的地方。如果需要的話,程序集會(huì)緩存。它只能存儲(chǔ)強(qiáng)命名程序集。它可以存儲(chǔ)同一程序集的不同版本。您可以使用gacutil.exe[3]自己將其安裝到GAC 。4.如果它是一個(gè)強(qiáng)名稱的程序集,并且配置文件包含<codeBase>節(jié)點(diǎn),那么它將檢查那里的程序集位置。如果該<codeBase>節(jié)點(diǎn)存在并且找不到程序集,FileNotFoundException則將引發(fā)a。5.根據(jù)啟發(fā)式算法檢查程序集DLL或EXE。此過(guò)程稱為“探測(cè)”。算法如下:1.檢查文件夾[application base] / [assembly name].dll。應(yīng)用程序庫(kù)是應(yīng)用程序可執(zhí)行文件所在的位置。通常,您的Bin \ Debug或Bin \ Release文件夾。2.檢查一下 [application base] / [assembly name] / [assembly name].dll3.如果為引用的程序集指定了區(qū)域性信息,則僅檢查以下目錄: [application base] / [culture] / [assembly name].dll [application base] / [culture] / [assembly name] / [assembly name].dll4.如果該<probing>節(jié)點(diǎn)存在于配置文件中,則它將在該privatePath節(jié)點(diǎn)的屬性指定的文件夾中查找程序集。

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

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

          ?為了確保您引用的是特定的程序集和版本,則將加載該確切版本。否則,將引發(fā)異常。而且,如果您知道自己在做什么,則可以在配置文件中指定覆蓋規(guī)則(綁定重定向)。?為了靈活地在您要加載的程序集中進(jìn)行。例如,如果要根據(jù)不同的區(qū)域性(語(yǔ)言)加載不同的程序集,則可以輕松地做到這一點(diǎn)?;蛘?,如果您要根據(jù)客戶配置加載不同的程序集,那也可以。?為了安全起見(jiàn),我們使用了全稱的程序集。他們確保您不能“偽造”程序集。例如,如果某個(gè)進(jìn)程希望加載Lib1 v4.5,那么您將無(wú)法加載具有相同名稱和版本的惡意軟件程序集。加載時(shí)會(huì)引發(fā)異常。這就是為什么在計(jì)算機(jī)上所有進(jìn)程都共享的GAC只接受強(qiáng)名稱程序集的原因。

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

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


          綁定重定向

          如果有一件事對(duì)于了解這筆交易非常重要,那就是綁定重定向。能夠告訴運(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,請(qǐng)參見(jiàn)該程序集已經(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,它就會(huì)嘗試綁定到version 1.2.0。

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



          圖片-20200711101325128


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

          當(dāng)問(wèn)題開(kāi)始發(fā)生時(shí)

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

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

          故障排除

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

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

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

          邊注

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

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

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

          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/

          回復(fù) 【關(guān)閉】學(xué)關(guān)
          回復(fù) 【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù) 【被刪】學(xué)個(gè)
          回復(fù) 【訪客】學(xué)
          回復(fù) 【小程序】學(xué)獲取15套【入門(mén)+實(shí)戰(zhàn)+賺錢(qián)】小程序源碼
          回復(fù) 【python】學(xué)微獲取全套0基礎(chǔ)Python知識(shí)手冊(cè)
          回復(fù) 【2019】獲取2019 .NET 開(kāi)發(fā)者峰會(huì)資料PPT
          回復(fù) 【加群】加入dotnet微信交流群

          副業(yè)剛需,沒(méi)有人能拒絕這個(gè)網(wǎng)站!


          終于GitHub App 已支持簡(jiǎn)體中文!


          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美乱伦黄色 | 午夜三级视频 | 插插插插亚洲综合 | 国产乱伦免费视频 | 久久ww|