您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關C#中AppDomain的作用是什么,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
應用程序域為安全性、可靠性、版本控制以及卸載程序集提供了隔離邊界。 應用程序域通常由運行時宿主創建,運行時宿主負責在運行應用程序之前引導公共語言運行時。
應用程序域所提供的隔離具有以下優點:
(1)在一個應用程序中出現的錯誤不會影響其他應用程序。 因為類型安全的代碼不會導致內存錯誤,所以使用應用程序域可以確保在一個域中運行的代碼不會影響進程中的其他應用程序。
(2)能夠在不停止整個進程的情況下停止單個應用程序。 使用應用程序域使您可以卸載在單個應用程序中運行的
注意:不能卸載單個程序集或類型。只能卸載整個域。
一切的根源,都是因為只有 Assembly.Load 方法,而沒有 Assembly.Unload 方法,只能卸載其所在的 AppDomain。
操作為讀取配置文件(為測試 AppDomain 中配置文件的讀取情況),并使用 Newtonsoft.Json 將其序列化為 json(為測試 AppDomain 中加載程序中的第三方引用情況),在控制臺輸出。項目名為 ReadPrint, 將其編譯為 exe 文件,并存放在 D:\AppDomainModules 中。
using Newtonsoft.Json; using System; using System.Configuration; namespace ReadPrint { class Program { static void Main(string[] args) { DoSomething(); } public static void DoSomething() { Person person = new Person { Account = ConfigurationManager.AppSettings["Account"], Name = ConfigurationManager.AppSettings["Name"], Age = int.Parse(ConfigurationManager.AppSettings["Age"]) }; Console.WriteLine(JsonConvert.SerializeObject(person)); Console.ReadLine(); } class Person { public string Account { get; set; } public string Name { get; set; } public int Age { get; set; } } } }
為了查看方便定義了 DoSomething 來執行相關方法。也可以直接寫在 Main 方法中,調用時需要傳入參數 args。因為最終測試 AppDomain 的程序也打算使用控制臺應用,也使用控制臺應用來寫這個小程序。
主要包含 AssemblyLoader.cs 文件用于封裝使用細節,和 Program.cs 主程序文件。
AssemblyLoader.cs
using System; using System.IO; using System.Reflection; namespace AppDomainTest { public class AssemblyDynamicLoader { private AppDomain appDomain; public readonly RemoteLoader remoteLoader; public AssemblyDynamicLoader() { AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationName = "ApplicationLoader"; setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules"); setup.CachePath = setup.ApplicationBase; setup.ShadowCopyFiles = "true"; # 重點 setup.ShadowCopyDirectories = setup.ApplicationBase; setup.ConfigurationFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", "ReadPrint.exe.config"); //AppDomain.CurrentDomain.SetShadowCopyFiles(); this.appDomain = AppDomain.CreateDomain("ApplicationLoaderDomain", null, setup); String name = Assembly.GetExecutingAssembly().GetName().FullName; this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName); # 重點 } public void Unload() { try { if (appDomain == null) return; AppDomain.Unload(this.appDomain); this.appDomain = null; } catch (CannotUnloadAppDomainException ex) { throw ex; } } } public class RemoteLoader : MarshalByRefObject { private Assembly _assembly; public void LoadAssembly(string assemblyFile) { try { _assembly = Assembly.LoadFrom(assemblyFile); } catch (Exception ex) { throw ex; } } public void ExecuteMothod(string typeName, string methodName) { if (_assembly == null) { return; } var type = _assembly.GetType(typeName); type.GetMethod(methodName).Invoke(Activator.CreateInstance(type), new object[] { }); } } }
其中類 RemoteLoader 為加載程序集的類,AssemblyDynamicLoader 類在此基礎上封裝了新建 AppDomain 的細節。
在 AssemblyDynamicLoader 的構造函數中,為了測試方便,硬編碼了一些內容,如 程序集文件查找路徑 PrivateBinPath 為當前程序執行目錄下面的 Modules 目錄,配置文件 ConfigurationFile 為 Modules 目錄中的 ReadPrint.exe.config, 以及創建新 AppDomain 時的程序集名稱。
AppDomainSetup 的屬性 ShadowCopyFiles(似乎可以譯為“卷影復制”) 代表是否鎖定讀取的程序集。如果設置為 true,則將程序集讀取至內存,不鎖定其文件,這也是熱更新的前提;否則在程序執行期間這些程序集文件會被鎖定,不能變化。
AppDomain 的方法 CreateInstanceAndUnwrap 意為在 AppDomain 的實例中創建指定類型的新實例,并返回。
在 RemoteLoader 的 ExecuteMethod 中,傳入的參數硬編碼為空。在實際使用時應當根據實際傳入參數。
Program.cs
using System; using System.IO; namespace AppDomainTest { class Program { static void Main(string[] args) { string modulesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules"); DirectoryInfo di = new DirectoryInfo(modulesPath); if (!di.Exists) { di.Create(); } string remotePath = @"D:\AppDomainModules\"; string[] fileNames = new string[] { "ReadPrint.exe", "Newtonsoft.Json.dll", "ReadPrint.exe.config" }; foreach(var fileName in fileNames) { FileInfo fi = new FileInfo(Path.Combine(remotePath, fileName)); fi.CopyTo(Path.Combine(modulesPath, fileName), true); } AssemblyDynamicLoader adl = new AssemblyDynamicLoader(); adl.remoteLoader.LoadAssembly(Path.Combine(modulesPath, "ReadPrint.exe")); adl.remoteLoader.ExecuteMethod("ReadPrint.Program", "DoSomething"); adl.Unload(); } } }
在主程序文件中,創建 Modules 文件夾,拷貝程序文件、庫文件和配置文件。程序運行結果:
可以看到成功調用了我們定義的 DoSomething 方法。
使用此方法,會首先在主程序的 AppDomain 中加載一遍程序集(和依賴),再移至我們創建的 AppDomain 中(特別注意,此時不會從我們新建的 AppDomain 的 PrivateBinPath 中搜索和加載)。
缺點有二,一是隨著程序的運行,可能會加載大量的程序集,因此主程序的 AppDomain 也要加載大量程序集,而程序集無法單獨卸載,只有在主程序停止后才會卸載,其間必然越積越多,極不優雅;二是無法自定目錄,主程序加載程序集和依賴時只會在其指定的 PrivateBinPath 中搜索,因此其它模塊所有需要的程序集文件都堆積在同一個目錄中,條理不清。
驗證
修改 AssemblyDynamicLoader.cs 中的代碼,改為直接在構造函數里面執行程序加載,其它不變,并查看我們新建的 AppDomain 中已加載的程序集:
//String name = Assembly.GetExecutingAssembly().GetName().FullName; //this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName); Assembly assembly = this.appDomain.Load("ReadPrint"); Type t = assembly.GetType("ReadPrint.Program"); MethodInfo mi = t.GetMethod("DoSomething"); //mi.Invoke(Activator.CreateInstance(t), new object[] { }); var tmp = this.appDomain.GetAssemblies();
此處最為奇怪的是,盡管我們在上面指定了自己 AppDomain 的 PrivateBinPath 和 配置文件,執行時依然找的是主程序的 PrivateBinPath 和 配置文件,因此將執行的那一行代碼注釋。
修改 Program.cs 中的代碼,改為僅調用 AssemblyDynamicLoader 的構造函數,其它不變,并查看主程序 AppDomain 中已加載的程序集:
AssemblyDynamicLoader adl = new AssemblyDynamicLoader(); //adl.remoteLoader.LoadAssembly(Path.Combine(modulesPath, "ReadPrint.exe")); //adl.remoteLoader.ExecuteMethod("ReadPrint.Program", "DoSomething"); //adl.Unload(); var tmp = AppDomain.CurrentDomain.GetAssemblies(); Console.ReadLine();
結果如圖所示:
需要注意的是,RemoteLoader 類繼承了 MarshalByRefObject,而繼承此類的應用可以跨 AppDomain 使用。此處猜測雖然可以在主程序中創建新的 AppDomain,但新的 AppDomain 依然無法完全擺脫主程序。
上述就是小編為大家分享的C#中AppDomain的作用是什么了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。