Assigning UI Button Listeners in for loop results in incorrect pass

I am creating buttons and assigning their listeners at runtime, but the code below, for some unknown reason, causes every button to be passed the value from the last item in the list ‘Abilities’.

So in the bottom function, despite a different value in each loop, the same value is passed to the button listener.

void CreateButtons ()
{
	for(int i = 0; i < abilities.Count; i++)
	{
		Ability a = abilities*;*
  •   	string aName = a.name;*
    
  •   	GameObject go = (GameObject)Instantiate(abilityButton,Vector3.zero, Quaternion.identity);*
    
  •   	go.transform.SetParent(buttonLayout.transform);*
    
  •   	go.GetComponent<RectTransform>().localScale = Vector3.one;*
    
  •   	go.GetComponent<Button>().onClick.RemoveAllListeners();*
    
  •   	abilityButtons.Add(go);*
    
  •   	go.GetComponent<Image>().sprite = Resources.Load<Sprite>("Art/UI/Buttons/" + a.name);*
    
  •   	AddButtonListener(aName, go);*
    
  •   }*
    
  • }*
  • void AddButtonListener(string _name, GameObject buttonGO)*
  • {*
    Debug.Log(_name); // <------ This logs correctly a different value on every loop.
    //Where as this gives every button the same name argument, equal to the last in the list.
    buttonGO.GetComponent().onClick.AddListener(() => UseAbility(_name));
  • }*

Create a variable inside to AddButtonListener and simple assing it with _name then pass it to the callback, this way the variable should be captured (search for this term for more info on the subject).

void AddButtonListener(string _name, GameObject buttonGO) {
     string localName = _name;
     buttonGO.GetComponent<Button>().onClick.AddListener(() => UseAbility(localName));
}

Adding callbacks for buttons in a Coroutine also have its own pitfalls so beware.

Check this answer post closures - Captured variable in a loop in C# - Stack Overflow