Skip to content

闭包

unity-csharp-closures-system闭包是什么

闭包就是:一个函数记住了它外面的变量。

更准确一点:

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
  • 大量对象频繁注册闭包回调
  • 事件注册后忘记移除监听
  • 捕获了很大的对象,导致生命周期变长

新手口诀

回调以后才执行。 回调用了外面的变量。 这就是闭包。 写循环绑定按钮时,先复制一份局部变量。

参考链接:

文章评价

读完这篇,留下你的看法

暂无审核通过的评价。

登录账号后才能评价。

本站访客数0总站访问量0本页访问量0