再談CLR查找和加載程序集的方式


這是一個老問題,以前也有朋友寫過一些文章介紹,但可能還不是很全面。我也多次被人問到,這里結合案例再次談談,希望對大家有所幫助。

本文范例代碼可以通過這里下載 http://files.cnblogs.com/chenxizhang/AssemblyMatchDemoSolution.zip

根據程序集的特征,討論這個問題,我們大致上有兩個分類

沒有做強名稱簽名的程序集

對於這種情況,CLR查找和加載程序集的方式如下

  1. 程序的根目錄
  2. 根目錄下面,與被引用程序集同名的子目錄
  3. 根目錄下面被明確定義為私有目錄的子目錄

同時,這種情況下,如果有定義codebase,則codebase的優先級最高,而且如果codebase指定的路徑找不到,則直接報告錯誤,不再查找其他目錄

 

有做強名稱簽名的程序集

對於這種情況,CLR查找和加載程序集的方式如下

  1. 全局程序集緩存
  2. 如果有定義codebase,則以codebase定義為准,如果codebase指定的路徑找不到,則直接報告錯誤
  3. 程序的根目錄
  4. 根目錄下面,與被引用程序集同名的子目錄
  5. 根目錄下面被明確定義為私有目錄的子目錄

 

我們幫助大家更好地理解以上的說明,我准備用范例來做講解。

 

1.准備基本范例

下面的范例演示了一個應用程序(MyApplication),和一個類庫(MyLibrary) ,MyApplication是引用了MyLibrary的。

image

MyLibrary中有一個TestClass類型,提供了一個簡單的方法(SayHello)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyLibrary
{
    public class TestClass
    {
        public void SayHello()
        {
            //這里為了演示方便,顯示出來當前加載的程序集完整路徑
            Console.WriteLine(this.GetType().Assembly.Location);
            Console.WriteLine("Hello,world");
        }
    }
}

 

在MyApplication中,我們就是簡單地創建了這個類型的實例,然后調用方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            var c = new MyLibrary.TestClass();
            c.SayHello();

            Console.Read();
        }
    }
}

默認情況下,如果我們編譯整個項目,那么MyLibrary.dll會被自動地復制到MyApplication的根目錄,如下圖所示

image

運行MyApplication.exe,我們能看到下面這樣的輸出

image

我們可以很清楚地看到,當前加載的MyLibrary.dll是來自於MyApplication的根目錄的。

 

2. 假如我們不想將MyLibrary.dll放在應用程序的根目錄

有時候,我們會希望單獨存放MyLibrary.dll,那么第一種做法就是,直接在應用程序根目錄下面建立一個與程序集同名的子目錄,然后將程序集放進去。

image

我們注意到,根目錄下面的MyLibrary.dll 被移動到了MyLibrary目錄
image

然后,我們再次運行MyApplication.exe,能看到下面這樣的輸出:

image

 

3.假如我們有很多程序集,希望統一放在一個目錄

第二步的方法雖然不錯,但有一個問題,就是如果我們引用的程序集很多的話,就需要在根目錄下面建立很多子目錄。那么,有沒有辦法統一地將這些程序集放在一個目錄中呢?

我們可以通過如下的方式,定義一個特殊的私有路徑(PrivatePath)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="libs"></probing>
    </assemblyBinding>
  </runtime>
</configuration>

同時,我們將MyLibrary.dll 移動到libs這個子目錄下面去

image

然后,我們再次運行MyApplication.exe,能看到下面這樣的輸出:

image

 

這也就是說,對於沒有簽名的程序集,CLR一般會按照如下的規則查找和加載程序集

  1. 程序的根目錄
  2. 根目錄下面,與被引用程序集同名的子目錄
  3. 根目錄下面被明確定義為私有目錄的子目錄

但是,有一個例外

4. codebase的設置是優先的,而且是排他的

codebase是一個特殊的設置,我們可以在配置文件中明確地指定某個程序集的查找路徑,這個規則具有最高的優先級,而且如果你做了設置,CLR就一定會按照你的設置去查找,如果找不到,它就報告失敗,而不會繼續查找其他路徑。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="libs"/>

      <dependentAssembly>
        <assemblyIdentity name="MyLibrary"
                          culture="neutral" />
        <codeBase version="1.0.0.0"
                  href="CodeBase\MyLibrary.dll" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

 

image

 

請注意,我們保留了libs目錄和Mylibrary目錄,而且根目錄下面也保留了一個MyLibrary.dll。 實際上,當前我們一共有4個dll. 那么到底會加載哪一個呢?

image

這種情況下,如果codebase下面找不到MyLibrary.dll 會怎么樣呢?

image

我們發現他是會報告錯誤的,而不會查找其他目錄的程序集。

 

5.如果有強名稱簽名會怎么樣呢?

對程序集進行強名稱簽名的好處是,可以將其添加到全局全局程序集緩存中。這樣既可以實現程序集的共享,又可以從一定程度上提高性能。

image

簽名后,我們將其添加到全局程序集緩存中去

image

那么這種情況下,不管我們在應用程序根目錄(或者下面的子目錄)有沒有MyLibrary.dll ,CLR都是嘗試先從全局程序集緩存中查找和加載的。

image

 

需要注意的是,如果程序集是經過了強名稱簽名,則在定義codebase的時候,應該注明publicKeyToken

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="libs"/>

      <dependentAssembly>
        <assemblyIdentity name="MyLibrary"
                          publicKeyToken="4a77fca346941a6c"
                          culture="neutral" />
        <codeBase version="1.0.0.0"
                  href="CodeBase\MyLibrary.dll" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
 
 
 

總結

本文通過實例講解了CLR在查找和加載程序集的時候所遵循的一些規則,針對有強名稱和沒有強名稱的程序集,這些規則略有不同。本文范例代碼可以通過這里下載 http://files.cnblogs.com/chenxizhang/AssemblyMatchDemoSolution.zip

 

 

相關問題

本文還沒有涵蓋到的另外兩個特殊情況,在日常工作中不多見,大家有興趣可以再找些資料研讀。

1.在目錄中查找的時候,如果dll查找不到,則會嘗試查找同名的exe

2.如果程序集帶有區域性,而不是語言中立的,則還會嘗試查找以語言區域命名的子目錄。

通常情況下,我們都就是程序集設置為語言中立的,所以不存在這個問題

image

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM