闭包

闭包

闭包(Closure)是一个重要的编程概念,尤其在使用 C# 时。闭包是指一个函数可以捕获和记住它的作用域(上下文)中的变量,即使这个函数已经在其原始作用域之外被调用

闭包的基本概念

在 C# 中,匿名方法和 Lambda 表达式通常会创建闭包。闭包允许方法捕获局部变量并在稍后某个时刻使用这些变量。

示例:

  • 闭包:此时第一个for循环中Log的i将产生闭包“captured variable is modified in the outer scope”,即“捕获的变量在外部范围中被修改”。后续输出都是最后一个i值,因为闭包捕获的是变量 i 的引用,而不是当前值。

image-20250111205342902

  • 避免闭包:通过创建局部变量 temp,确保闭包捕获的是变量的值而不是引用。

image-20250111205151475

Unity开发中常见的闭包场景

1.延迟任务(例如协程)

闭包可以用于在延迟执行中捕获当前上下文变量,

    void Start()
    {
        for (int i = 5; i < 10; i++)
        {
            StartCoroutine(DelayedLog(x => i, i * 0.5f));//i被匿名方法捕获,延迟调用后i始终为10
        }
    }

    System.Collections.IEnumerator DelayedLog(Func<int,int> func, float delay)
    {
        yield return new WaitForSeconds(delay);
        Debug.Log($"Delayed index: {func.Invoke(0)}");//prints: Delayed index: 10
    }

2. 动态事件绑定

Unity 中的按钮事件或自定义事件监听器经常会用到闭包。

示例代码

csharp复制代码using UnityEngine;
using UnityEngine.UI;

public class ButtonClosureExample : MonoBehaviour
{
    public Button buttonPrefab;
    public Transform buttonParent;

    void Start()
    {
        for (int i = 0; i < 5; i++)
        {
            Button newButton = Instantiate(buttonPrefab, buttonParent);
            newButton.GetComponentInChildren<Text>().text = $"Button {index}";
            newButton.onClick.AddListener(() => Debug.Log($"Button {i} clicked"));
        }
    }
}

在这个例子中,会生成5个Button0~4的按钮,但是点击时输出的始终为Button 5 clicked

3. 异步任务(例如 UnityWebRequest 或异步加载)

在异步操作中,闭包可以帮助将参数传递给回调。

示例代码

csharp复制代码using UnityEngine;
using UnityEngine.Networking;

public class AsyncClosureExample : MonoBehaviour
{
    void Start()
    {
        string[] urls = { "https://example.com/file1", "https://example.com/file2" };
        foreach (string url in urls)
        {
            StartCoroutine(DownloadData(url));
        }
    }

    System.Collections.IEnumerator DownloadData(string url)
    {
        UnityWebRequest request = UnityWebRequest.Get(url);
        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            Debug.Log($"Downloaded from {url}: {request.downloadHandler.text}");
        }
        else
        {
            Debug.LogError($"Failed to download from {url}: {request.error}");
        }
    }
}

这里的 url 是通过闭包捕获的,每个协程实例会记住自己的 URL,即使下载任务是异步的。

总结

在 Unity 开发中,闭包是一种强大的工具,尤其在事件、协程、异步任务中非常有用。然而,使用闭包时需要小心捕获变量的行为,避免产生难以调试的错误。

posted @ 2025-01-11 21:33  世纪末の魔术师  阅读(70)  评论(0)    收藏  举报