For() loop and AddListener() (already tried temporary variables...)

Hi!
I’m trying to make a “list” of ui buttons;
I create them and add the click event in a for loop:

		for (int i = 0; i<profiles.Count; i++) {
			string _tempString = profiles*;*
  •   		Debug.Log (_tempString);*
    
  •   		GameObject button = (GameObject)Instantiate (Resources.Load ("ProfileButton"));*
    

_ button.name = profiles*;_
Button _tempButton = button.GetComponent();
_tempButton.onClick.AddListener(() => GameManager.Profiles.SetCurrentProfile (tempString));
_
}*

The Debug.Log print the correct string, buttons have the correct name, however, when I click on any button it sets the same profile (the last in profiles List)
I’m puzzled.
If anyone has an idea… :slight_smile:
Thanks in advance!

Apparently the problem was that the code was run inside a Coroutine.
I changed for a public void() and now it works.

Okay, this is an old question, but it comes out in Google, so I think a better explanation won’t hurt.

This is not a bug. It might be counter-intuitive, but every language with async capabilities will behave the same way. Here’s the sample program:

using System;
using System.Threading.Tasks;

class Script
{
    static public void Main(string[] args)
    {
        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Counter value: {0}", i);
        }

        Console.WriteLine("---");

        for (int i = 0; i < 5; i++) {
            Task.Run(() => Console.WriteLine("Counter value: {0}", i));
        }

        // Wait for user input so the program is not terminated
        Console.ReadLine();
    }
}

The output will be:

Counter value: 0
Counter value: 1
Counter value: 2
Counter value: 3
Counter value: 4
---
Counter value: 5
Counter value: 5
Counter value: 5
Counter value: 5
Counter value: 5

Why is that? When a for loop is executed, variable i is allocated on the stack, and it’s incremented on every pass. In the first case, when we use it immediately, the current value of i is used (and the variable itself is still kept on the stack).

Now, if you declare an action that will take place somewhere in the future (timeout, event listener, whatever), and this action will make use of i, system will move i to the heap and it will be accessed in async code by reference (i.e. address in memory). All 5 handlers declared in this loop will reference the same i, which will have the value of 5 by the end of the loop (it’s incremented from 4 to 5, thus stops satisfying condition i < 5 and only then loop breaks).

This might not be what you expected, but this is what it must be.

Now, the answer to the question “what do I do to have a distinct value in every async task” is simple: you need to copy the value of i outside the async code on every iteration:

for (int i = 0; i < 5; i++) {
    var j = i;
    Task.Run(() => Console.WriteLine("Counter value: {0}", j));
}

A new variable j will be created on every pass now, and its value will be the copy of current value of i (note: this only works for scalars, i.e. integers, floats/doubles, booleans; if i was an object, you would need to do var j = i.Clone(), otherwise j will be merely a reference to i). The output in this example will be something like this:

Counter value: 1
Counter value: 3
Counter value: 2
Counter value: 0
Counter value: 4

Hope this helps.