That’s one of the oddities how lambda variable captures work in C#. All the handlers you register refer to the same variable i even though it’s a local variable and normally wouldn’t exist after Awake is done exucuting. It would probably also work without introducing additional function if you did something like this:
int i = 0;
foreach (Button btn in myButtons)
{
int j = i;
btn.onClick.AddListener(() => { MyFunc(j); });
i++;
}
It is not unique to C# there are other programming languages using similar approach. It has it’s uses typically involving modification of captured variable. If I remember correctly javascript, python and swift behave similarly. Java forces captured variables to be final thus preventing such error. C++ and Rust allows you to choose if you want to capture the value or reference and makes you being a bit more explicit with it.