Weird behaviour when using button.onClick.AddListener

Hi,

I’m trying to add a listener function to buttons on runtime.

On ‘Awake()’ or ‘Start()’ I have tried the following:

m_buttons is a list of buttons. (assume size of 4)

for(int i=0;i<m_buttons.Count;++i)
{
            m_buttons[i].onClick.AddListener(delegate{this.OnSelect(m_buttons[i]);});
}

the issue here is that I get index out of range error when clicking the buttons.

However, the following (should be functionally the same) works without problems:

m_buttons[0].onClick.AddListener(delegate{this.OnSelect(m_buttons[0]);});
m_buttons[1].onClick.AddListener(delegate{this.OnSelect(m_buttons[1]);});
m_buttons[2].onClick.AddListener(delegate{this.OnSelect(m_buttons[2]);});
m_buttons[3].onClick.AddListener(delegate{this.OnSelect(m_buttons[3]);});

Another issue I have is the following:

(Assume each button has a different name: ex. button1,button2, button3, button4)

OnSelect(Button sender)
{
  Debug.Log(sender.name);
}

Doing this:

foreach(Button b in m_buttons)
{
   b.onClick.AddListener(()=>OnSelect(b));
}

Makes every button I press log the name of the last button in the list (Button4).

I have used the above in a different project with no problems, not sure why this is happening. If anyone has any insights on what the cause may be please let me know, thanks!

**I tested the above in blank project just to be sure nothing else is interfering, but I am still getting the same problems. Is this a bug??

**using Unity 4.6.0 f3 (OS X)

I’ve seen this once before and never got to the bottom it. Could be a mono compiler issue.
I’d log it to be on the safe side and ensure the team know to check it out.

This has happened to me as well, it has to do with the lambda of the for and foreach statements, I still have yet to understand whats going wrong, so I typically work around using it in that form.

The reason this is happening is because the loop variable that’s passed to the anonymous function is declared outside the loop, so will always hold its last value when the function is eventually called post-loop. If you declare another variable inside the loop to capture its current state when the function is declared, it should work fine e.g.

foreach(Button b in m_buttons)
{
    Button b2 = b;
    b2.onClick.AddListener(()=>OnSelect(b2));
}
2 Likes

First off, in your for loop, change ++i to i++. The two are functionally different. Look up the difference, and you’ll understand why you’re getting an index out of range error.

While you are right about pre and post increment being different, it actually makes no difference which one you use in a for loop. (This is because the evaluation and incrementation is decoupled).

But as noony already pointed out this is not a bug, but due to the access of a modified closure.