多次加载程序集的语义

Semantics of loading an assembly more than once

我正在开发一个插件系统,用户很可能会展示包含 classes 的新程序集,这些程序集在名称空间下的名称完全相同,因为他们正在安装已安装内容的更新版本。

如果您加载的程序集包含已由已加载的其他程序集定义的 classes,预期行为是什么?

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

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

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

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

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

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

下面的实验

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, Culture=neutral, PublicKeyToken=null
System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Runtime.Loader, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Runtime.Extensions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
System.Console, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
(2) XYZ.StepHandlers, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
False

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

正在查看 Assembly.CreateInstance

的文档

If the runtime is unable to find typeName in the Assembly instance, it returns null instead of throwing an exception.

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

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


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