Appearance
闭包
闭包是什么
闭包就是:一个函数记住了它外面的变量。
更准确一点:
c
闭包 = 函数 + 它捕获的外层变量例子:
c
int count = 0;
Action add = () =>
{
count++;
};
add();这里 () => { count++; } 是一个 Lambda,它用到了外面的 count,所以它捕获了 count,形成闭包。
Unity 里最常见的闭包
按钮点击时记住某个编号:
c
private void Bind(Button button, int levelId)
{
button.onClick.AddListener(() =>
{
OpenLevel(levelId);
});
}按钮以后才会被点击,但这个 Lambda 已经记住了 levelId。
闭包最经典的坑
在 for 循环里直接捕获 i:
c
for (int i = 0; i < buttons.Count; i++)
{
buttons[i].onClick.AddListener(() =>
{
SelectItem(i);
});
}容易出问题,因为闭包捕获的是变量 i,不是当时那个数字。多个按钮可能都拿到同一个最终的 i。
安全写法:
c
for (int i = 0; i < buttons.Count; i++)
{
int index = i;
buttons[index].onClick.AddListener(() =>
{
SelectItem(index);
});
}每一轮都创建一个新的 index,每个按钮回调捕获自己的那一份。
闭包看到的是变量最新值
c
int value = 1;
Action log = () =>
{
Debug.Log(value);
};
value = 5;
log();通常输出 5,不是 1。 因为闭包捕获的是变量本身,不是简单复制一份旧值。
什么时候用闭包
适合:
- UI 按钮回调记住编号
- 事件监听记住上下文
- 网络返回后使用请求时的数据
- 延迟执行时保留局部信息
- 给 Lambda 传一点外部环境
谨慎使用:
Update()里每帧创建 Lambda- 大量对象频繁注册闭包回调
- 事件注册后忘记移除监听
- 捕获了很大的对象,导致生命周期变长
新手口诀
回调以后才执行。 回调用了外面的变量。 这就是闭包。 写循环绑定按钮时,先复制一份局部变量。
参考链接: