在 Unity 中,如果你在运行时使用代码动态生成了一个程序集(Assembly),并希望在运行时重新加载它,需要了解以下几点:


🔧 一、Unity 的限制

Unity 的运行时(Mono 或 IL2CPP)不支持卸载已经加载的程序集。也就是说,一旦通过 Assembly.Load 加载了程序集,就无法卸载或替换它,除非整个 AppDomain 被卸载,而 Unity 不允许你创建/卸载自定义 AppDomain


🧪 二、可行方案

✅ 方法一:使用 Assembly.Load(byte[]) 加载编译好的 DLL

你可以在运行时生成 DLL,然后用 Assembly.Load 加载它:

byte[] dllBytes = File.ReadAllBytes("path/to/your.dll");
Assembly assembly = Assembly.Load(dllBytes);
Type myType = assembly.GetType("YourNamespace.YourClass");
object instance = Activator.CreateInstance(myType);

这个只能加载一次,无法重复加载不同版本的 DLL(名字相同会冲突)。


✅ 方法二:用 AssemblyLoadContext(仅限 .NET Core/5+/Unity Future)

Unity 当前默认不支持 AssemblyLoadContext,但如果未来升级到 .NET Core 支持,它将允许卸载和重新加载程序集。


✅ 方法三:通过子进程实现热重载

一个变通方法是将你的热更新逻辑放到一个独立的子进程(例如使用 .NET CLI 程序),通过进程间通信(如 Socket、NamedPipe)与 Unity 主进程交互,实现真正意义的“热重载”。


🔁 Unity 编辑器下的变通方案

如果你是在编辑器中自动生成脚本代码,然后希望重新编译/加载这些代码,可以使用:

UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();

这个会触发 Unity 的重新编译,自动将新生成的脚本编译到当前的 Assembly-CSharp.dll 中。


📦 如果你使用了 Assembly Definition 文件(.asmdef)

你也可以通过代码控制 .asmdef 的增删,然后调用 Unity 的编译流程强制重新生成并加载该程序集。


🔄 更推荐的运行时代码更新方案:使用热更新框架

如果你的目标是运行时代码更新,推荐使用以下热更框架:

框架名 优势
ILRuntime 支持 Unity,稳定,支持 AOT 与 JIT
HybridCLR 支持热更新、可替换函数、接近原生性能
XLua 使用 Lua 作为热更语言,灵活轻便

✅ 总结

目标 推荐方案
编辑器生成代码后加载 UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation()
运行时加载一次 Assembly.Load(byte[])
多次加载/卸载 不可行(Mono 无法卸载程序集)
需要动态替换逻辑 使用 ILRuntime、HybridCLR 等热更框架

CompilationPipeline.RequestScriptCompilation()EditorUtility.RequestScriptReload()核心区别:


✅ 一览对比表:

特性 CompilationPipeline.RequestScriptCompilation() EditorUtility.RequestScriptReload()
所属命名空间 UnityEditor.Compilation UnityEditor
功能 请求重新编译脚本 请求重新加载脚本(通常用于强制刷新域)
是否触发编译 ✅ 是 ❌ 不一定
是否触发 reload 编译成功后自动 reload 立即触发 reload(前提是脚本已编译)
使用场景 你有改动脚本或生成代码后需要 Unity 重新编译 脚本没有变化,但你想强制 Unity 刷新一下脚本域
是否常用 常用于代码生成工具(如生成 .cs 后自动编译) 常用于脚本域恢复(如反序列化、内存刷新)
是否立即生效 编译是异步进行 reload 是立即排队进行

🎯 具体解释

1️⃣ CompilationPipeline.RequestScriptCompilation()

  • 作用:告诉 Unity “我改动了脚本,请你编译一下”。

  • 典型使用场景

    • 在编辑器扩展中自动生成 .cs 脚本后调用它;
    • 修改了脚本文件后通过代码触发 Unity 自动编译;
    • 通常用于配合 .asmdef 结构进行自定义编译处理。

📝 示例:

UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();

Unity 编译完成后,会自动触发 Domain Reload(重载脚本域)。


2️⃣ EditorUtility.RequestScriptReload()

  • 作用:告诉 Unity “请你重新加载一下脚本(或刷新 C# Domain)”,但不会强制编译

  • 典型使用场景

    • 没有代码变更但你想强制刷新脚本状态(比如刷新 [InitializeOnLoad] 类);
    • 有时在一些特殊状态下(如某些 ScriptableObject 状态出错)用它强制刷新;
    • 不能用于生成代码的场景,因为它不会触发编译器动作

📝 示例:

UnityEditor.EditorUtility.RequestScriptReload();

如果 Unity 没检测到 .cs 文件的变化,这个方法执行后可能没有任何可见效果。


✅ 总结建议

你想做的事 使用哪个方法
你用代码生成了 .cs 文件,希望 Unity 编译并生效 RequestScriptCompilation()
你没有修改代码,只是希望 Unity 重新加载脚本(如热刷新初始化类) RequestScriptReload()
希望模拟 Unity 脚本更新后的自动编译效果 RequestScriptCompilation()

如你在 Unity 编辑器中自动生成代码并希望它立即生效,推荐顺序是

// 生成文件后
AssetDatabase.Refresh(); // 刷新资产
CompilationPipeline.RequestScriptCompilation(); // 请求编译

⚠️ 一般不建议手动调用 RequestScriptReload(),除非你遇到 Unity 编辑器某些奇怪状态,正常流程应由 Unity 自动触发 reload。

如果你想实现完整的生成代码 ➜ 自动编译 ➜ 编译完成后再做某事的流程,也可以配合监听 CompilationPipeline.compilationFinished

好的!以下是一个 完整示例,展示如何在 Unity 编辑器中:

  1. 生成一段 .cs 脚本文件
  2. 自动刷新资源(AssetDatabase.Refresh)
  3. 请求脚本编译(CompilationPipeline.RequestScriptCompilation)
  4. 在编译完成后执行某些操作(如打印信息、绑定数据、生成 ScriptableObject 等)。

✅ 示例:Unity 编辑器代码生成 + 自动编译监听

using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
using System.IO;

public class AutoScriptGenerator : EditorWindow
{
    [MenuItem("Tools/自动生成脚本并编译")]
    public static void GenerateScriptAndCompile()
    {
        string scriptPath = "Assets/Generated/MyGeneratedClass.cs";

        // 1. 写入脚本内容
        string scriptContent = @"
using UnityEngine;

public class MyGeneratedClass
{
    public static void SayHello()
    {
        Debug.Log(""Hello from generated class!"");
    }
}";
        File.WriteAllText(scriptPath, scriptContent);
        Debug.Log("✅ 已生成脚本:" + scriptPath);

        // 2. 刷新资源数据库
        AssetDatabase.Refresh();

        // 3. 注册编译完成回调(注意:只注册一次,避免重复执行)
        CompilationPipeline.compilationFinished -= OnCompilationFinished; // 避免重复绑定
        CompilationPipeline.compilationFinished += OnCompilationFinished;

        // 4. 请求 Unity 重新编译脚本
        CompilationPipeline.RequestScriptCompilation();
    }

    // 编译完成回调
    private static void OnCompilationFinished(object context)
    {
        Debug.Log("✅ 脚本编译完成!");

        // 取消监听(只监听一次)
        CompilationPipeline.compilationFinished -= OnCompilationFinished;

        // ✅ 编译完成后你可以执行一些初始化逻辑:
        // 比如自动生成 ScriptableObject、初始化数据等
        // 下面这个类可以通过反射拿到
        var type = System.Type.GetType("MyGeneratedClass");
        if (type != null)
        {
            var method = type.GetMethod("SayHello", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
            method?.Invoke(null, null); // 执行静态方法
        }
    }
}

🧪 使用方法

  1. 创建一个编辑器脚本,保存为 Assets/Editor/AutoScriptGenerator.cs
  2. 在 Unity 菜单栏中点击 Tools → 自动生成脚本并编译
  3. 脚本会自动生成,并请求 Unity 编译;
  4. 编译完成后会执行 MyGeneratedClass.SayHello() 方法并打印日志。