在.NET中重新加载程序集而不创建新的AppDomain

本文关键字:程序集 创建 AppDomain 加载 NET 新加载 | 更新日期: 2023-09-27 18:20:49

我正在C#中开发一个游戏引擎+编辑器。其想法是,用户将所有游戏代码写入一个项目中,然后编辑器将加载该项目,这样它就可以列出用户定义的类,并在场景编辑器中实例化它们。

考虑到(重新)加载DLL的概念,我可以加载DLL,在外部程序集中定义的类上实例化和调用方法,重建外部程序集,然后在主机中再次加载它,而无需重新启动主机。这项工作(至少看起来像),但它并没有从以前加载的程序集中释放内存。

装载机类别:

public class Loader
{
    // This DLL contains an implementation of ILoadable - ILoadable is declared in a shared project.
    private const string AsmPath = @"D:'Dev'AsmLoading'AsmLoadingImpl'bin'Debug'AsmLoadingImpl.dll";
    public ILoadable GetExternalLoadable()
    {
        var asmBytes = File.ReadAllBytes(AsmPath);
        Assembly asm = Assembly.Load(asmBytes, null);
        var loadableInterface = typeof(ILoadable);
        var loadableImpl = asm.GetTypes().First(loadableInterface.IsAssignableFrom);
        return (ILoadable)Activator.CreateInstance(loadableImpl);
    }
}

运行GetExternalLoadable() 2或3次,任务管理器显示我的主机程序中的RAM使用量增加了约1mb,重复相同的操作会进一步增加,而不会减少。

有什么办法解决这个问题吗?我知道Unity也在做类似的事情,只是他们实际上自己编译外部程序集,但当我触发几次重新编译时,Unity编辑器不会消耗额外的内存。

因此,我试图实现的是外部程序集的"实时重新加载"

在.NET中重新加载程序集而不创建新的AppDomain

.NET4添加了对可回收程序集的支持,这些程序集可以由GC卸载。

然而,这些都受到严格限制:

  • 它们用于动态代码生成,不能只加载.dll文件
  • 它们不能定义COM互操作类型、P/Invoke方法、线程静态字段等

如果你有一个遵循这些限制的.dll,你可能能够加载并重新发出IL代码,使其看起来像是.NET.的动态生成的程序集

除了动态方法和可收集程序集之外,如果不卸载整个AppDomain,就不可能卸载代码。