Unity重新加载脚本程序集后List异常问题求助
Unity脚本程序集重载后异常的解决方案
问题根源
Unity脚本程序集重载时会重新编译生成新程序集,原场景/预制体中的脚本实例会被Unity尝试对象迁移,但自定义泛型集合、异步任务对象等复杂类型的序列化/反序列化容易失效,导致旧对象实例与新类型不兼容,进而出现null引用、索引异常等问题。
异常一:List出现null值
原因
重载前添加到列表的Task实例属于旧程序集类型,重载后新代码中的Task是新程序集类型,Unity自动序列化时无法将旧实例映射到新类型,最终转为null。
解决方法
- 禁用自动序列化:给列表添加
[NonSerialized]属性,避免Unity在重载时尝试序列化该列表,再在Awake或OnEnable中手动初始化:
using System.Collections.Generic; using UnityEngine; public class TaskManager : MonoBehaviour { [NonSerialized] public List<Task> QueuedTasks = new(); private void Awake() { // 确保重载后列表被重新初始化 QueuedTasks = QueuedTasks ?? new List<Task>(); } }
- 清理无效实例:监听程序集重载事件,在重载完成后自动移除列表中的null对象:
using UnityEngine; using System.Collections.Generic; using UnityEditor; public class TaskManager : MonoBehaviour { public List<Task> QueuedTasks = new(); private void OnEnable() { AssemblyReloadEvents.afterAssemblyReload += CleanInvalidTasks; } private void OnDisable() { AssemblyReloadEvents.afterAssemblyReload -= CleanInvalidTasks; } private void CleanInvalidTasks() { QueuedTasks.RemoveAll(task => task == null); } }
异常二:Contains返回true但Remove抛出索引异常
原因
重载后,列表中的旧Task实例虽然在Contains判断中返回true(可能因重写了Equals方法或Unity的对象引用处理逻辑),但该实例属于旧程序集,与新代码中的类型不匹配,调用Remove时内部索引计算出错。
解决方法
- 使用唯一标识替代引用判断:给
Task类添加唯一ID,通过ID判断对象是否相等,而非直接使用引用或默认Equals:
public class Task { public Guid TaskId { get; } = Guid.NewGuid(); // 重写Equals和GetHashCode,基于TaskId判断 public override bool Equals(object obj) { return obj is Task task && TaskId.Equals(task.TaskId); } public override int GetHashCode() { return TaskId.GetHashCode(); } }
- 重载后重建队列:在程序集重载完成后,直接清空队列并重新初始化,避免旧实例残留:
private void CleanInvalidTasks() { QueuedTasks.Clear(); // 如果需要恢复任务,可从持久化存储中重新加载 }
- 改用安全的移除方式:避免直接调用
Remove(task),改用索引遍历移除或RemoveAll批量处理:
// 替代QueuedTasks.Remove(task) QueuedTasks.RemoveAll(t => t?.TaskId == task?.TaskId);
内容的提问来源于stack exchange,提问作者Clément Andraud
相关产品推荐
相关产品推荐

