標籤:main 9.png .com turn 項目 assembly dom file 報錯
我們知道反射是 依賴注入 模式的基礎,依賴注入要求只在項目中引用定義介面的程式集,而不引用介面實作類別的程式集,因為介面實作類別的程式集應該是通過反射來動態載入的,這樣才能保證介面與其實作類別之間的松耦合。可是有時候我們使用反射動態載入程式集的時候會失敗,因為除非我們手動將介面實作類別的程式集放在項目產生後的bin目錄下,或者是在GAC中,否者.Net Framework並不知道該到哪裡去尋找介面實作類別的dll組件檔。幸運的是我們如果使用 AppDomain.CurrentDomain.AssemblyResolve事件,就可以通過C#代碼來自訂程式集載入邏輯,當C#反射解析程式集失敗的時候,通過執行自訂程式集載入邏輯來找到相應的程式集dll檔案。
例如現在我們定義了一個普通的C#類庫項目叫MessageDisplay,如所示
裡面只包含了一個C#類MessageDisplayHelper.cs檔案,MessageDisplayHelper.cs代碼如下:
using System;namespace MessageDisplay{ public class MessageDisplayHelper { public string Display() { return "This is a message!"; } }}
然後我們定義一個C#控制台程式叫AssemblyResolverConsle:
在這個控制台程式中我們不直接引用MessageDisplay程式集,而是使用反射載入程式集MessageDisplay,然後使用反射動態構造MessageDisplayHelper類。由於我們沒有在控制台程式AssemblyResolverConsle中直接引用MessageDisplay程式集,所以在調用Assembly.Load方法動態載入程式集的時候會失敗,從而觸發AppDomain.CurrentDomain.AssemblyResolve事件。AssemblyResolverConsle控制台程式的Program.cs檔案代碼如下:
using System;using System.Reflection;namespace AssemblyResolverConsle{ class Program { static void Main(string[] args) { //當程式集通過反射載入失敗的時候會觸發AssemblyResolve事件,這裡註冊AssemblyResolve事件的處理函數為CurrentDomain_AssemblyResolve AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; //這裡通過調用Assembly.Load方法反射載入MessageDisplay程式集會失敗,因為本項目中沒有引用該程式集,而且MessageDisplay程式集的dll檔案也不在本項目產生的bin目錄下,也不在GAC中。所以這裡會觸發AssemblyResolve事件,調用處理函數CurrentDomain_AssemblyResolve來嘗試執行自訂程式集載入邏輯,然後處理函數CurrentDomain_AssemblyResolve會為這裡的Assembly.Load方法返回MessageDisplay.dll程式集 var messageDisplayAssembly = Assembly.Load("MessageDisplay"); //使用反射動態調用MessageDisplayHelper類的建構函式 var messageDisplayHelper = messageDisplayAssembly.CreateInstance("MessageDisplay.MessageDisplayHelper"); Console.WriteLine(messageDisplayHelper.ToString()); Console.ReadLine(); } /// <summary> /// AssemblyResolve事件的處理函數,該函數用來自訂程式集載入邏輯 /// </summary> /// <param name="sender">事件引發源</param> /// <param name="args">事件參數,從該參數中可以擷取載入失敗的程式集的名稱</param> /// <returns></returns> private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { //根據載入失敗程式集的名字找到該程式集並返回 if (args.Name == "MessageDisplay") { //我們自訂的程式集載入邏輯知道MessageDisplay程式集在C:\AssemblyResolverConsle\Reference\MessageDisplay.dll這個路徑下,所以這裡載入這個路徑下的dll檔案作為AssemblyResolve事件處理函數的傳回值 return Assembly.LoadFile(@"C:\AssemblyResolverConsle\Reference\MessageDisplay.dll"); } //如果AssemblyResolve事件的處理函數返回null,說明AssemblyResolve事件的處理函數也無法找到載入失敗的程式集,那麼整個程式就會拋出異常報錯 return null; } }}
所以AppDomain.CurrentDomain.AssemblyResolve這個事件,給反射載入程式集失敗提供了一個很好的解決途徑,可以允許開發人員自訂程式集解析邏輯。我們不再需要把一個.Net程式所需要用到的所有dll檔案都要求放到bin目錄下或GAC中,而是可以放在任何位置,通過AppDomain.CurrentDomain.AssemblyResolve事件的處理函數來動態載入。
使用C#的AssemblyResolve事件動態解析載入失敗的程式集