多次加载程序集的语义

问题描述

我正在开发一个插件系统,用户很可能会展示包含命名空间完全相同的类的新程序集,因为他们正在安装已经安装的东西的更新版本。

当您加载包含已加载的其他程序集定义的类的程序集时,预期的行为是什么?

在 DNFX 中,这是由 AppDomains 明确定义的,但它们在 netcore 中已被 AssemblyLoadContexts 取代,不是程序集执行上下文。

当我两次加载同一个程序集时,没有报告异常,但我想知道我是否替换了类或没有效果,以及任何其他语义。欢迎提供建议的研究术语或相关文档的链接。

到目前为止,我发现了这个:https://github.com/dotnet/runtime/issues/39783

似乎是说使用 AssemblyLoadContexts 可以多次加载程序集,但它没有说明这对于具有相同全名的两个类以及实例化它们的代码意味着什么。

我想当我加载一个程序集对象并显式地将它用于 CreateInstance 时,我将获得该程序集中定义的类,但我仍然希望查看有关此的文档。

解决方法

我想我已经为自己解决了这个问题。

下面的实验

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;

namespace reload
{
  class Program
  {
    static void Main(string[] args)
    {
      var alc1 = new ALC();
      var alc2 = new ALC();
      Assembly assy1,assy2;
      using (var stream = new FileStream("./assy.dll",FileMode.Open))
        assy1 = alc1.LoadFromStream(stream);
      using (var stream = new FileStream("./assy.dll",FileMode.Open))
        assy2 = alc2.LoadFromStream(stream);
      var currentDomain = AppDomain.CurrentDomain;
      foreach (var item in currentDomain.GetAssemblies())
      {
        Console.WriteLine($"{item.FullName}");
      }
      Console.WriteLine(assy1 == assy2);
    }
  }
  public class ALC : AssemblyLoadContext
  {
    public ALC() : base(isCollectible: true) { }
    protected override Assembly Load(AssemblyName name) => null;

  }
}

产生这个

System.Private.CoreLib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=7cec85d7bea7798e
reload,Version=1.0.0.0,PublicKeyToken=null
System.Runtime,Version=4.2.2.0,PublicKeyToken=b03f5f7f11d50a3a
System.Runtime.Loader,Version=4.1.1.0,PublicKeyToken=b03f5f7f11d50a3a
System.Runtime.Extensions,PublicKeyToken=b03f5f7f11d50a3a
System.Console,Version=4.1.2.0,PublicKeyToken=b03f5f7f11d50a3a
(2) XYZ.StepHandlers,Version=1.2.3.4,PublicKeyToken=null
False

从这个结果可以明显看出,加载了两个不同的 XYZ.StepHandlers 程序集,并且它们不是同一个对象。

查看 Assembly.CreateInstance 的文档

如果运行时无法在 Assembly 实例中找到 typeName,它将返回 null 而不是抛出异常。

这证实了我的怀疑,即在程序集对象上调用 CreateInstance 将严格解析该程序集对象内的类型。在具有项目引用的程序集的普通代码中,提供程序集的解析取决于程序集仅加载一次的假设。

在插件场景中,我们可以违反这个假设。但是我们已经有了对程序集的显式引用,所以没有歧义。


如果您想知道,我不会让他们无缘无故地加载重复项。当我得到一个新程序集时,我必须加载它以提取程序集名称和版本,并确定它是新的还是替换现有的(更不用说它是否实际上是一个兼容插件而不是一些随机的 DLL)。>

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...