-1

I have an array of Button objects that I want to pass a function with different arguments into. This function takes an int argument. I've tried the following:

public void ButtonClick(int i){
  //do things with value of i
}

public void FillButtonClicks(){
  for(int i = 0; i < 3; i++){
    ButtonArray[i].AddListener(delegate{ButtonClick(i);});
  }
}

This "works" in that the buttons receive the appropriate delegates, but it does NOT work in that the garbage collector is holding onto the value of 'i' in the for loop, i.e., when I click any of the three buttons, they all execute ButtonClick(3). Is there a way to enforce the passing of values, or do I have to just do something dumb like:

ButtonArray[0].AddListener(delegate{ButtonClick(0);});    
ButtonArray[1].AddListener(delegate{ButtonClick(1);});    
ButtonArray[2].AddListener(delegate{ButtonClick(2);});
  • Have you looked into using Button.CommandName or Button.CommandArgument instead of passing the value to the function directly? – Brian Heward Sep 18 '17 at 19:48
  • Not sure what you mean by these suggestions. Fruchtzwerg's answer solved the problem though. – Dylan Reedy Sep 18 '17 at 20:00
  • Depending on the API you are using, the Button class often has public parameters you can use to save either a string (Button.CommandName) or an object (Button.CommandArgument) value that the button click handlers has access to. – Brian Heward Sep 18 '17 at 20:07

1 Answers1

1

You need to make a local copy of i inside the loop.

public void FillButtonClicks(){
  for(int i = 0; i < 3; i++){
    int j = i;
    ButtonArray[j].AddListener(delegate{ButtonClick(j);});
  }

The reason: ButtonClick is called after the loop already iterated through and the parameter is the current value of i (after all iterations the last index). This behaviour is a little bit confusing but totally correct like described by Eric Lippert at Closing over the loop variable considered harmful.

Fruchtzwerg
  • 10,999
  • 12
  • 40
  • 49