原文:
原文:https://www.cnblogs.com/1996V/p/9037603.html
什么是BCL
當你通過VS創建一個項目后,你這個項目就已經引用好了通過.NET下的語言編寫好的一些類庫。比如控制台中你直接就可以用ConSole類來輸出信息,或者using System.IO 即可通過File類對文件進行讀取或寫入操作,這些類都是微軟幫你寫好的,不用你自己去編寫,它幫你編寫了一個面向.NET的開發語言中使用的基本的功能,這部分類,我們稱之為BCL(Base Class Library), 基礎類庫,它們大多都包含在System命名空間下。
基礎類庫BCL包含:基本數據類型,文件操作,集合,自定義屬性,格式設置,安全屬性,I/O流,字符串操作,事件日志等的類型
System.Object的意義
說起類型,這里要說CTS定義的一個非常重要的規則,就是類與類之間只能單繼承,System.Object類是所有類型的根,任何類都是顯式或隱式的繼承於System.Object。
System.Object定義了類型的最基本的行為:用於實例比較的Equals系列方法、用於Hash表中Hash碼的GetHashCode、用於Clr運行時獲取的類型信息GetType、用於表示當前對象字符串的ToString、用於執行實例的淺復制MemberwiseClone、用於GC回收前操作的析構方法Finalize 這6類方法。
所以 Object不僅是C#語言的類型根、還是VB等所有面向.NET的語言的類型根,它是整個FCL的類型根。
當然,CTS定義了單繼承,很多編程語言都滿足這個規則,但也有語言是例外,如C++就不做繼承限制,可以繼承多個,C++/CLI作為C++在對.NET的CLI實現,如果在非托管編碼中多繼承那也可以,如果試圖在托管代碼中多繼承,那就會報錯。我前面已經舉過這樣特殊情況的例子,這也在另一方面反映出,各語言對CTS的支持並不是都如C#那樣全面的,我們只需明記一點:對於符合CTS的那部分自然就按照CTS定義的規則來。 任何可遵循CTS的類型規范,同時又有.NET運行時的實現的編程語言就可以成為.NET中的一員。
程序集的加載方式
對於自身程序集內定義的類型,我們可以直接從自身程序集中的元數據中獲取,對於在其它程序集中定義的類型,CLR會通過一組規則來在磁盤中找到該程序集並加載在內存。
CLR在查找引用的程序集的位置時候,第一個判斷條件是 判斷該程序集是否被簽名。
什么是簽名?
程序集搜索規則
事實上,按照存儲位置來說,程序集分為共享(全局)程序集和私有程序集。
CLR查找程序集的時候,會先判斷該程序集是否被強簽名,如果強簽名了那么就會去共享程序集的存儲位置(后文的GAC)去找,如果沒找到或者該程序集沒有被強簽名,那么就從該程序集的同一目錄下去尋找。
強名稱程序集是先找到與程序集名稱(VS中對項目右鍵屬性應用程序->程序集名稱)相等的文件名稱,然后 按照唯一標識再來確認,確認后CLR加載程序集,同時會通過公鑰效驗該簽名來驗證程序集是否被篡改(如果想跳過驗證可查閱https://docs.microsoft.com/zh-cn/dotnet/framework/app-domains/how-to-disable-the-strong-name-bypass-feature),如果強名稱程序集被篡改則報錯。
而弱名稱程序集則直接按照與程序集名稱相等的文件名稱來找,如果還是沒有找到就以該程序集名稱為目錄的文件夾下去找。總之,如果最終結果就是沒找到那就會報System.IO.FileNotFoundException異常,即嘗試訪問磁盤上不存在的文件失敗時引發的異常。
注意:此處文件名稱和程序集名稱是兩個概念,不要模棱兩可,文件CLR頭內嵌程序集名稱。
舉個例子:
我有一個控制台程序,其路徑為D:\Demo\Debug\demo.exe,通過該程序的元數據得知,其引用了一個程序集名稱為aa的普通程序集,引用了一個名為bb的強名稱程序集,該bb.dll的強名稱標識為:xx001。
現在CLR開始搜索程序集aa,首先它會從demo.exe控制台的同一目錄(也就是D:\Demo\Debug\)中查找程序集aa,搜索文件名為aa.dll的文件,如果沒找到就在該目錄下以程序集名稱為目錄的目錄中查找,也就是會查 D:\Demo\Debug\aa\aa.dll,這也找不到那就報錯。
然后CLR開始搜索程序集bb,CLR從demo.exe的元數據中發現bb是強名稱程序集,其標識為:xx001。於是CLR會先從一個被定義為GAC的目錄中去通過標識找,沒找到的話剩下的尋找步驟就和尋找aa一樣完全一致了。
當然,你也可以通過配置文件config中(配置文件存在於應用程序的同一目錄中)人為增加程序集搜索規則:
1.在運行時runtime節點中,添加privatePath屬性來添加搜索目錄,不過只能填寫相對路徑:
<runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="relative1;relative2;" /> //程序集當前目錄下的相對路徑目錄,用;號分割 </assemblyBinding> </runtime>
2.如果程序集是強簽名后的,那么可以通過codeBase來指定網絡路徑或本地絕對路徑。
<runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="myAssembly" publicKeyToken="32ab4ba45e0a69a1" culture="neutral" /> <codeBase version="2.0.0.0" href="http://www.litwareinc.com/myAssembly.dll" /> </dependentAssembly> </assemblyBinding> </runtime>
當然,我們還可以在代碼中通過AppDomain類中的幾個成員來改變搜索規則,如AssemblyResolve事件、AppDomainSetup類等。
有關運行時節點的描述:https://docs.microsoft.com/zh-cn/dotnet/framework/configure-apps/file-schema/runtime/runtime-element