You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

捕获Lambda中变量时触发MissingReferenceException问题排查

问题(翻译后)

我参考了一篇Stack Overflow帖子尝试在Lambda中访问局部变量的值,却触发了如下异常:

MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.

我的代码如下:

private static void ValidateComponent<T>(Transform transform, Delegate validationMethod, object[] args) where T : Component
{
#if UNITY_EDITOR // EditorApplication is only available when running in the editor
    if (EditorApplication.isPlaying) return;

    if (transform == null) return; // in case inspector field is not assigned

    var t = transform;

    EditorApplication.delayCall += () =>
    {
        var component = t.GetComponent<T>();

        if (component == null)
        {
            LogAddingComponent<T>(t);
            var addedComponent = t.gameObject.AddComponent<T>();
            validationMethod.DynamicInvoke(args.Prepend(addedComponent));
        }
        else
        {
            validationMethod.DynamicInvoke(args.Prepend(component));
        }
    };
#endif
}

请问我哪里出错了?


问题原因

你通过var t = transform;复制的只是引用地址,而非Transform对象本身——因为Transform是Unity的引用类型。当EditorApplication.delayCall执行时(下一次编辑器刷新时),原Transform对应的GameObject可能已经被用户删除或销毁,此时t的引用指向的是一个已标记为销毁的对象,就会触发MissingReferenceException。

另外,Unity编辑器中对象销毁后,引用不会立刻变为null(这是编辑器的“虚引用”机制,用于提示对象已销毁),所以你注册delayCall前的if (transform == null)判断,无法覆盖“注册后对象才被销毁”的场景。


解决方法

方案1:通过实例ID验证对象存活

注册delayCall前记录对象的实例ID,执行时通过ID查找对象,确认是否还存在:

private static void ValidateComponent<T>(Transform transform, Delegate validationMethod, object[] args) where T : Component
{
#if UNITY_EDITOR
    if (EditorApplication.isPlaying) return;
    if (transform == null) return;

    var instanceId = transform.GetInstanceID();
    var originalArgs = args;
    var targetMethod = validationMethod;

    EditorApplication.delayCall += () =>
    {
        // 通过实例ID查找对象,判断是否存活
        var t = EditorUtility.InstanceIDToObject(instanceId) as Transform;
        if (t == null) return;

        var component = t.GetComponent<T>();
        if (component == null)
        {
            LogAddingComponent<T>(t);
            var addedComponent = t.gameObject.AddComponent<T>();
            targetMethod.DynamicInvoke(originalArgs.Prepend(addedComponent));
        }
        else
        {
            targetMethod.DynamicInvoke(originalArgs.Prepend(component));
        }
    };
#endif
}

方案2:使用弱引用持有对象

利用WeakReference持有对象引用,既不会阻止对象被销毁,又能在执行时检查对象状态:

private static void ValidateComponent<T>(Transform transform, Delegate validationMethod, object[] args) where T : Component
{
#if UNITY_EDITOR
    if (EditorApplication.isPlaying) return;
    if (transform == null) return;

    var weakTransform = new WeakReference(transform);
    var originalArgs = args;
    var targetMethod = validationMethod;

    EditorApplication.delayCall += () =>
    {
        if (!weakTransform.IsAlive) return;
        var t = weakTransform.Target as Transform;
        if (t == null) return;

        var component = t.GetComponent<T>();
        if (component == null)
        {
            LogAddingComponent<T>(t);
            var addedComponent = t.gameObject.AddComponent<T>();
            targetMethod.DynamicInvoke(originalArgs.Prepend(addedComponent));
        }
        else
        {
            targetMethod.DynamicInvoke(originalArgs.Prepend(component));
        }
    };
#endif
}

额外注意点

  • 要把validationMethod和args也复制到局部变量(如示例中的originalArgs、targetMethod),避免外部上下文变化导致的意外问题。
  • 编辑器环境下delayCall的执行时机存在时间差,这段时间内用户完全可能修改或销毁对象,所以必须做有效性校验。

内容的提问来源于stack exchange,提问作者Wojtek Wencel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 14:55:22