如何在不使用Assembly.Load的情况下加载程序集?

可以通过将程序集流式传输到内存中来实现吗? 如果是这样,那怎么做呢?

原因:

我不想锁定加载的DLL。 我希望能够动态加载,更改代码,再次编译和重新加载它

它可以通过使用字节数组的Load重载来完成。 您需要在加载之前读取汇编字节,它不会锁定文件:

byte[] readAllBytes = File.ReadAllBytes("path"); Assembly assembly = Assembly.Load(readAllBytes); 

据我了解,您想要执行以下操作:

  1. 将程序集从磁盘加载到内存中,以便使用其中的数据或调用其中的代码
  2. 以后能够卸载组件
  3. 避免将组件锁定在磁盘上,以便您可以修改它而无需退出应用程序(或首先卸载组件)

基本上,您所描述的是各种插件系统,您可以使用阴影dll和应用程序域来实现。

首先,为了卸载程序集,不需要退出应用程序,您需要将该程序集加载到单独的应用程序域中。 你应该能够在网上找到关于如何做到这一点的好教程。

这是一个谷歌查询 ,应该为您提供一些起始文章。

其次,为了避免将程序集锁定在磁盘上,这很简单,只需先复制它,然后加载副本而不是原始副本。 当然,你会锁定副本,但副本只是你的应用程序的临时文件,所以没有人应该对修改该文件感兴趣。 这使原始文件解锁并可修改。

您应该尝试使用阴影而不是使用Assembly.Load的重载,如果您有多个将要加载和替换的程序集,则可以从字节数组加载程序集。

例如,如果您的插件程序集A.dll依赖于第二个程序集B.dll,并且在调用Assembly.Load之前使用字节数组技巧将A.dll加载到内存中,则需要在应用程序中处理程序集解析调用domain(你可以在需要加载程序集时告诉你,并且“帮助”加载过程),或者你需要确保B.dll的加载方式与加载A.dll的方式相同,否则从中加载A.dll内存将自动从磁盘加载B.dll。


以下是有关使用单独的应用程序域的更多信息。

当您创建另一个应用程序域时,通过在.NET中使用AppDomain类,您将在内存中创建一个单独的隔离专区,您可以在其中运行代码。 它与您的主应用程序域实际上是分开的,并且只有一个穿过隔离墙的小孔。

通过这个洞,您可以传递消息,如方法调用和数据。

构建新的应用程序域后,在其中加载一个或多个程序集。 通常,如果要为此类加载构建要加载到其中的程序集,则将加载1;如果尚未加载,则加载2(下面将详细介绍)。

加载程序集后,在另一个应用程序域中构造一个或多个对象,使第一个应用程序域可以与这些对象进行通信。 这些对象需要从MarshalByRefObjectinheritance , MarshalByRefObject是一个允许一些魔法发生的类。

基本上会发生这种情况。 在其他应用程序域内,创建了加载到该应用程序域中的类型的对象。 此类型来自MarshalByRefObject 。 构造此对象的请求来自第一个应用程序域,并且在此应用程序域内部构造了一个代理对象,该对象看起来像是在同一个应用程序域中创建的同一对象。 代理通过那个洞与另一个对象交谈。

所以现在你有两个应用程序域和两个对象,每一个对应一个,并且这些对象相互通信。

通过此设置,稍后您可以切断对象之间的连接,然后卸载其他应用程序域,这基本上会拆除该隔离专区。 然后,如果您愿意,可以构建一个新的第二个应用程序域,然后重新开始,实际上再次从磁盘重新加载程序集。

需要注意的一件事是你通过这个洞的数据。 如果您通过该孔传递的任何数据是在您加载的程序集(您的插件或扩展程序集)中声明的对象,那么您不仅会将该对象返回到您的主应用程序域,您的主应用程序域也将将该程序集加载到其自己的域中,从而使重新加载后无法正确地与第二个应用程序域通信。

因此,请确保不要这样做,传递本机类型或在要替换的程序集之外定义的类型。

我提到你可能想要加载至少两个程序集。 这背后的原因是,如果你想构造一个对象的类型,你要加载的那个程序集中声明的类型,不会从MarshalByRefObject下降,那么再次通过该洞传递类型的问题就会出现,并且您也可以将程序集加载到主域中。 处理此问题的一种典型方法是使用某种插件管理器类,它确实来自MarshalByRefObject ,并让此管理器位于其他域中并与其他类型进行通信。 这避免了通过该洞传递类型的问题。

我已经在这里漫游了一段时间,所以我会留下它,但是通过这些信息,您应该能够理解并利用通过Google查询找到的文章更容易一些。

您可以创建程序集的临时副本,并使用Assembly.Load加载该副本。 将文件监视器放在原始文件上,并在文件监视器检测到更改时卸载/重新加载更新的程序集的临时副本。

如果您从源代码编译DLL,您甚至不必复制,而是重新编译过程应如下所示:

1)销毁加载了程序集的应用程序域,有效地解锁DLL。

2)重新编译脚本。

3)为插件重新创建应用程序域,并在其中加载新程序集。