Unity动态生成按钮点击触发ArgumentOutOfRangeException异常
问题根因
这是C#循环绑定事件时的闭包变量捕获经典问题,和目标业务函数、buttonActions列表初始化逻辑无关。
你在循环生成按钮、绑定点击回调的逻辑中,直接捕获了循环迭代用的索引变量。回调实际触发(用户点击生成的按钮)时,循环逻辑早已执行完毕,此时索引变量的值为循环终止后的数值:你本次生成1个按钮,循环结束后索引值自增为1,而长度为1的buttonActions列表合法索引范围仅为0,访问索引1自然触发ArgumentOutOfRangeException。
场景中手动放置的按钮不存在循环绑定的变量捕获逻辑,因此调用目标函数完全正常。
修复方案
- 循环内声明临时变量拷贝当前索引,回调绑定该临时变量而非直接捕获循环迭代变量,参考代码如下:
// 原错误写法(对应脚本第86行的问题逻辑) for (int i = 0; i < categoryButtonConfigs.Count; i++) { Button newBtn = Instantiate(buttonPrefab, buttonContainer); // 直接捕获循环变量i,点击触发时i已变为循环结束后的终值 newBtn.onClick.AddListener(() => buttonActions[i].Invoke()); } // 修复后写法 for (int i = 0; i < categoryButtonConfigs.Count; i++) { int currentIndex = i; // 每次循环迭代独立持有当前索引值,不受后续循环自增影响 Button newBtn = Instantiate(buttonPrefab, buttonContainer); newBtn.onClick.AddListener(() => buttonActions[currentIndex].Invoke()); }
- 更稳妥的写法:生成按钮时直接持有当前要绑定的
Action委托,回调直接触发持有的委托,完全绕开索引访问逻辑,从根源避免索引问题:
for (int i = 0; i < categoryButtonConfigs.Count; i++) { Button newBtn = Instantiate(buttonPrefab, buttonContainer); Action clickAction = buttonActions[i]; newBtn.onClick.AddListener(() => clickAction.Invoke()); }
验证方法
修复后可在按钮点击回调内加日志打印当前访问的索引值,点击"Next Song"按钮时日志输出索引为0即符合预期,可正常调用切歌逻辑,不会再抛出索引越界异常。
内容的提问来源于stack exchange,提问作者Evelyn
相关产品推荐
相关产品推荐

