1

I came across this puzzling behavior while making a simulation project in Unity3D.

Basically, I was creating anonymous functions in a loop using the variable being looped, and add all these functions to a queue for processing.
The curious case in Unity3D Monobehavior is that they are only called on the last looped variable.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class TestDist : MonoBehaviour {

    int counter = 0;

    // Use this for initialization
    void Start () {

        List<LOL> lols = new List<LOL>();
        lols.Add(new LOL(1));
        lols.Add(new LOL(2));
        lols.Add(new LOL(3));

        List<Func<int>> fs = new List<Func<int>>();
        foreach (LOL l in lols)
        {
            //****** Critical Section *********
            Func<int> func = () =>
            {
                Debug.Log(l.ID);   //Prints "3" 3 times instead of
                return 0;          // "1", "2", "3"
            };
            //****** Critical Section *********

            fs.Add(func);
        }
        foreach (Func<int> f in fs)
        {
            f();
        }
    }

    // Update is called once per frame
    void Update () {
    }
}

class LOL
{
    public long ID;
    public LOL(long id)
    {
        ID = id;
    }
}

The code works good in plain Visual Studio Console application, but fails on Unity3D. (Version 5.0) by printing the last value ("3") 3 times instead of 1,2,3.
I tried various methods to circumvent and the following worked for no apparent reason : Changing the Critical Section to the following block solves the issue:

LOL another = l;
Func<int> func = () =>
{
    Debug.Log(another.ID);
    return 0;
};

Will be delighted if someone can answer (Unity Bug Fix Team does not seem to care)

1 Answer 1

0

You are facing the problem of closure.

See this question : What are 'closures' in C#?

Try this instead. Its merely the same, but not completely since you declare a "new" lol in your loop :

for( int index = 0 ; index < lols.Count ; ++index )
{
    Lol l = lols[index];
    Func<int> func = () =>
    {
        Debug.Log(l.ID);
        return 0;
    };
    fs.Add(func);
}

If it does not work, try :

foreach (LOL l in lols)
{           
    fs.Add(GetFunction(l.ID));
}

// ...

private Func<int> GetFunction( int identifier)
{     
    Func<int> func = () =>
    {
        Debug.Log(identifier);
        return 0;
    };
    return func ;
}

I does not feel comfortable with closure, I must admit.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.