C# 卸载使用Assembly.LoadFrom()加载的程序集

C# 卸载使用Assembly.LoadFrom()加载的程序集,c#,garbage-collection,load,gettype,C#,Garbage Collection,Load,Gettype,我需要检查加载dll后运行GetTypes()的时间量。 代码如下 Assembly assem = Assembly.LoadFrom(file); sw = Stopwatch.StartNew(); var types1 = assem.GetTypes(); sw.Stop(); double time1 = sw.Elapsed.TotalMilliseconds; 我想卸载并重新加载dll,以检查再次运行GetTypes()所花费的时间 我怎样才能卸下它assem=null足够好

我需要检查加载dll后运行GetTypes()的时间量。 代码如下

Assembly assem = Assembly.LoadFrom(file);
sw = Stopwatch.StartNew();
var types1 = assem.GetTypes();
sw.Stop();
double time1 = sw.Elapsed.TotalMilliseconds;
我想卸载并重新加载dll,以检查再次运行GetTypes()所花费的时间

  • 我怎样才能卸下它
    assem=null
    足够好吗
  • 是否有明确的方法调用垃圾收集器来回收分配给assem的资源

    • 不幸的是,一旦加载程序集,就无法卸载它。但您可以卸载AppDomain。您可以做的是创建一个新的AppDomain(AppDomain.CreateDomain(…),将程序集加载到此AppDomain以使用它,然后在需要时卸载AppDomain。卸载AppDomain时,将卸载所有已加载的程序集。(见附件)

      要调用垃圾收集器,可以使用

      GC.Collect(); // collects all unused memory
      GC.WaitForPendingFinalizers(); // wait until GC has finished its work
      GC.Collect();
      

      GC在后台线程中调用终结器,这就是为什么您必须等待并再次调用Collect(),以确保删除了所有内容。

      您无法从当前AppDomain卸载程序集。但您可以创建新的AppDomain,将程序集加载到其中,在新AppDomain中执行一些代码,然后卸载它。检查以下链接:

      是否可以使用其他AppDomain

      AppDomain dom = AppDomain.CreateDomain("some");     
      AssemblyName assemblyName = new AssemblyName();
      assemblyName.CodeBase = pathToAssembly;
      Assembly assembly = dom.Load(assemblyName);
      Type [] types = assembly.GetTypes();
      AppDomain.Unload(dom);
      

      您可以将
      Load
      File.ReadAllBytes()一起使用,而不是使用
      LoadFrom()
      LoadFile()
      。这样,它不使用程序集文件,但将读取它并使用读取数据

      然后,您的代码将如下所示

      Assembly assem = Assembly.Load(File.ReadAllBytes(filePath));
      sw = Stopwatch.StartNew();
      var types1 = assem.GetTypes();
      sw.Stop();
      double time1 = sw.Elapsed.TotalMilliseconds;
      
      从中,我们无法卸载该文件,除非卸载该文件包含的所有域


      希望这能有所帮助。:

      程序集很遗憾无法卸载,而且,如果您使用appdomains,那么它将阻止您与主应用程序的api/程序集通信

      有关问题的最佳描述可在此处找到:

      脚本托管指南

      如果您想在不与主应用程序通信的情况下运行C#代码,那么最好的方法是集成C#脚本API:

      对于集成,您需要以下软件包:

      C#脚本:

      Visual studio扩展:

      然而,如果您希望从C#脚本到应用程序进行通信,那么使用相同的appDomain并不断更改程序集名称是目前唯一的方法,但不幸的是,这会占用ram和磁盘空间

      代码示例如何执行此操作可以在此处找到:

      CsScriptHotReload.sln

      下面是演示视频:

      如果只希望加载初始程序集而不加载其任何从属程序集,则可以在AppDomain中使用
      assembly.LoadFile
      ,然后在完成后卸载AppDomain

      创建加载程序类以加载和使用程序集:

      类加载器:MarshallByRefObject
      {
      公共无效加载(字符串文件)
      {
      var assembly=assembly.LoadFile(文件);
      //对组件进行处理。
      }
      }
      
      在单独的应用程序域中运行加载程序,如下所示:

      var domain=AppDomain.CreateDomain(nameof(Loader)、AppDomain.CurrentDomain.defence、new-AppDomainSetup{ApplicationBase=Path.GetDirectoryName(typeof(Loader.Assembly.Location)});
      试一试{
      var loader=(loader)domain.CreateInstanceAndUnwrap(typeof(loader).Assembly.FullName,typeof(loader).FullName);
      loader.Load(myFile);
      }最后{
      卸载(域);
      }
      
      简而言之,您不能卸载程序集;您只能卸载AppDomains。当您将程序集加载到新的AppDomain中时,会受到许多限制,因此我建议您对此主题进行一些研究,以了解它们是如何工作的。也就是说,您可以再次加载相同的程序集,即使之前的程序集没有卸载。是否有人尝试过如果您将程序集设为null,会发生什么情况?他们是否以任何方式收集或释放垃圾?我担心内存。一篇非常相关的帖子。对我来说失败了:它抛出dom。加载一个“FileNotFoundException”。有趣的是,该文件确实存在,异常消息包含该文件的确切版本(不是1.0.0.0,所以我确信它最终找到了我的文件…)。在我的例子中,“FileNotFoundException”是由引用的程序集之一引起的。最后,我使用Assembly.ReflectionOnlyLoadFrom与一个方法结合使用,该方法将所有引用的程序集加载到仅反射上下文中。出于某种原因,这将加载应用程序目录中具有相同名称(如前所述)的程序集,而不是上述目录中的程序集…知道为什么会发生这种情况吗?不幸的是,这不适用于.NET Core,因为AppDomains没有实现。您不能将字节数组传递给
      assembly.LoadFrom()
      。我编辑了你的帖子,但有人把它改了回去。@roli09实际上,你可以,而且效果很好。我动态加载DLL作为插件,然后当卸载它们时,我应该删除该文件。Hardik方法工作得很好,没有AppDomain方面的麻烦。roli09您应该在对注释进行任何修改之前测试代码,这应该是正确的答案,@tapatio它可以工作,因为Hardik使用的是
      Assembly.Load
      ,而不是
      Assembly.LoadFrom
      。@Hardik是正确的:Assembly Load方法保存了对加载文件的引用,防止该文件被其他进程覆盖或删除。因此,使用File对象的ReadAllBytes方法是一个聪明的解决方法,它不包含引用。读取这些字节并关闭文件后,结果将传递给程序集加载方法。相当聪明!这现在是最好的解决方案,尽管它目前只有不到领先答案的一半。我正在加载(并可能卸载)几十个DLL。比如说,创建70个AppDomains会对性能产生什么影响(以及其他相关影响)?如果可能的话我应该避免这样做吗?我想如果只是为了这个手术