page contents

C#中AppDomain的妙用

本文讲述了C#中AppDomain的妙用!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

attachments-2022-09-dld3cbSR6334fc760faf2.png

本文讲述了C#中AppDomain的妙用!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

基本概念

应用程序域为安全性、可靠性、版本控制以及卸载程序集提供了隔离边界。 应用程序域通常由运行时宿主创建,运行时宿主负责在运行应用程序之前引导公共语言运行时。

应用程序域所提供的隔离具有以下优点:

(1)在一个应用程序中出现的错误不会影响其他应用程序。 因为类型安全的代码不会导致内存错误,所以使用应用程序域可以确保在一个域中运行的代码不会影响进程中的其他应用程序。

(2)能够在不停止整个进程的情况下停止单个应用程序。 使用应用程序域使您可以卸载在单个应用程序中运行的

注意:不能卸载单个程序集或类型。只能卸载整个域。

一切的根源,都是因为只有 Assembly.Load 方法,而没有 Assembly.Unload 方法,只能卸载其所在的 AppDomain。

实践

1. 首先准备一个控制台小程序

操作为读取配置文件(为测试 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 的程序也打算使用控制台应用,也使用控制台应用来写这个小程序。

2. 编写使用 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 文件夹,拷贝程序文件、库文件和配置文件。程序运行结果:

attachments-2022-09-6vPHCqQL6334fbc7946ac.png

可以看到成功调用了我们定义的 DoSomething 方法。

更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2022-09-29 10:01
  • 阅读 ( 383 )
  • 分类:C/C++开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
轩辕小不懂
轩辕小不懂

2403 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1470 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章