Untiy 当以遍历的方法给多个按钮添加事件 并且把i当参数传入时报错

本文主要针对的问题是在Unity中对Button类进行Onclick事件绑定的时候出现的函数参数错误进行分析解决,具体问题如下:

Button[] button = GetComponentsInChildren();
int buttonCnt = 3;

for (int i = 0; i < buttonCnt; i++)
{

Debug.Log("i: " + i);
button[i].OnClick.AddListener( () => ClickButton(i) );

}

public void ClickButton(int addScore)
{

Debug.Log("addScore: " + addScore);
score = score + addScore;

}
这段代码中主要对buttonCnt个按钮绑定Onclick事件,期望用户在点击不同按钮的时候能给score加上不同的分数,但是实际上不管在点击任何按钮以后都会得到如下输出:

i: 0
i: 1
i: 2
addscore: 3
与我们的预期不符,我们期待在按不同按钮的时候能得到按钮的id:i,但是实际上我们不管按哪个按钮都会得到一个奇怪的数字3。如果我们仔细观察,可以注意到3恰好是遍历结束以后i的值,那这其中到底发生了什么呢?

原因
出现上述问题的原因主要与闭包(Closure)的特性有关。闭包的定义如下

闭包是一个绑定到声明它的环境中的函数。因此,该函数可以在其函数体中引用环境中的元素。换句话说,闭包是指有权访问另一个函数作用域中的变量的函数

上一段代码实际上是声明了一个匿名方法,再把这个匿名方法作为Onclick的事件。而匿名方法是一个闭包,并且绑定到它的父方法体和其中的局部变量上。其中我们需要注意的是:匿名方法绑定到变量上,而不是值。换句话说,当“ClickButton”被声明时,“i”的值没有被复制进来。相反,匿名方法将使用对“i”的引用,这样“ClickButton”将始终使用“i”的最新值。因此在给按钮进行事件绑定以后,不管用户对哪个按钮进行点击,都会调用"i"的最新值,也就是3。事实上,即使“i”超出了作用域,对“i”的引用也将持续发挥作用,如以下例子:

delegate void Action();

static Action GetAction()
{

int i = 0;

Action a = delegate {
Console.WriteLine(i); };

i = 1;

return a;
}

static void Main(string[] args)
{

Action a = GetAction();

a();
}
尽管在"a"被调用的位置已经不在局部变量"i"的作用域,但是输出仍然是1而不是0

解决方案
在了解了闭包的特性以后要解决这个问题就变得很简单了,只需要保证在给ClickButton传参的时候传入一个新的变量,而不是一个持续在使用的变量就可以了,因此可以将

button[i].OnClick.AddListener(delegate {
() => ClickButton(i); });
修改为

int temp = i
button[i].OnClick.AddListener(delegate {
() => ClickButton(temp); });
这样每次传入ClickButton的都是一个重新声明的局部变量,可以很好地解决这个问题

转载https://cxyzjd.com/article/shanwenkang/116739829

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值