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

本文关键字:加载 程序集 Assembly LoadFrom 卸载 | 更新日期: 2023-09-27 17:58:43

加载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的资源

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

你能使用另一个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);

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

然后你的代码将看起来像

Assembly assem = Assembly.Load(File.ReadAllBytes(filePath));
sw = Stopwatch.StartNew();
var types1 = assem.GetTypes();
sw.Stop();
double time1 = sw.Elapsed.TotalMilliseconds;

从这里我们不能卸载文件,除非它包含的所有域都被卸载。

希望这能有所帮助。:)

很遗憾,一旦加载程序集,就无法卸载它。但是您可以卸载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中执行一些代码,然后卸载它。查看以下链接:MSDN

如果只想加载初始程序集而不加载其任何依赖程序集,则可以在AppDomain中使用Assembly.LoadFile,然后在完成后卸载AppDomain。

创建一个加载程序类来加载并使用程序集:

class Loader : MarshalByRefObject
{
    public void Load(string file)
    {
        var assembly = Assembly.LoadFile(file);
        // Do stuff with the assembly.
    }
}

在一个单独的应用程序域中运行加载器,如下所示:

var domain = AppDomain.CreateDomain(nameof(Loader), AppDomain.CurrentDomain.Evidence, new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(typeof(Loader).Assembly.Location) });
try {
    var loader = (Loader)domain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
    loader.Load(myFile);
} finally {
    AppDomain.Unload(domain);
}
不幸的是,

Assembly无法卸载,而且,如果您使用appdomains,它将阻止您与主应用程序的api/Assembly进行通信。

关于问题的最佳描述可以在这里找到:

脚本托管指南http://www.csscript.net/help/Script_hosting_guideline_.html

若你们想在并没有和主应用程序通信的情况下运行C#代码,那个么最好的方法就是集成C#脚本API:

https://github.com/oleg-shilo/cs-script/tree/master/Source/deployment/samples/Hosting/Legacy%20Samples/CodeDOM/Modifying%20script%20without%20restart

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

C#脚本:http://www.csscript.net/CurrentRelease.html

Visual studio扩展:https://marketplace.visualstudio.com/items?itemName=oleg-shilo.cs脚本

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

代码示例如何做到这一点可以做到-可以从这里找到:

https://github.com/tapika/cppscriptcoreCsScriptHotReload.sln

这是演示视频:

https://drive.google.com/open?id=1jOECJj0_UPNdllwF4GWb5OMybWPc0PUV