Home > Net >  MissingReferenceException when trying to capture a variable in a lambda
MissingReferenceException when trying to capture a variable in a lambda

Time:12-04

I'm trying to access a value of a local variable inside a lambda according to this post, but I get this excepton:

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

Here is my code:

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
}

What am I doing wrong?

CodePudding user response:

You are attempting to pin transform via the variable t only to use it later during your delayCall callback. That's dangerous considering many types in Unity contain unmanaged resources hence the error. The effect is the same if you created a Font in WinForms, Disposed it, then attempted to use the font.

OP:

Is there a better way to approach this then?

From what I understand, Transforms are always attached to the GameObject and it is the GameObject that is being destroyed taking the Transform along with it.

The only way I know to prevent game object destruction is viaDontDestroyOnLoad but I'm not sure if its applicable here since the latter is really designed for protection whilst transitioning to a new scene, but you never know perhaps it prevents things from being destroyed in other scenarios.

Try calling DontDestroyOnLoad at the top of ValidateComponent like so:

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

    DontDestroyOnLoad (transform.gameObject); // <---- new
    var t = transform;
.
.
.

Otherwise, there is this Unity Answer, that suggests simply disabling and re-enabling the object rather than whatever is destroying the object in the first place. Following that approach you shouldn't require any changes to your code above.

Editor-time-only scripts

BTW, regarding the #if UNITY_EDITOR bit, if you want to avoid having to decorate your code with #ifs everywhere (as an ex c/c programmer I certainly don't miss having to do that all the time), consider simply moving your script into a child folder called Editor. Unity automatically treats anything placed in such folders as Editor-time-only and won't be incorporated into your final game build. Better yet, no need for the #if stuff.

The best reason for it though is to prevent build errors when Unity is producing your final game due to forgetting to either use #if or Editor folders. I typically run into this one with 3rd party scripts obtained from the Unity store. Instead of having to fix the code I simply move it to a child Editor folder.

See also

  • Related