C#delegate闭包|堆和栈

做项目写代码写了一个以为错的实际是对的代码,用委托来传参给我看疑惑了,那个变量明明到下一次循环就被销毁了,怎么会还能调到它的值。


先贴几个概念

回调函数 :一个函数作为参数传递给另一个函数,等到某个事件发生时,这个函数会被调用("回调")。Button.onClick.AddListener()参数就是一个回调函数。

委托 :简单来说类似于"方法的指针"。当使用 AddListener(delegate { ... }) 时,delegate { ... } 就是匿名回调函数。下面这个代码就是我在回调函数内部调用了 OnButtonClicked 并且传递了参数。

C#中的delegate、event、action及匿名函数(lambda表达式)_c# event action-CSDN博客

cs 复制代码
 private void GenerateButton(int optionIndex)
    {
        for (int i = optionIndex; i < dialogDataRows.Length; i++)//把挨着的分支全部找出来
        {
            string[] cells = dialogDataRows[i].Split(',');
            
                //给按钮绑定按下事件
            button.GetComponent<Button>().onClick.AddListener(delegate
            {
                    OnButtonClicked(int.Parse(cells[(int)TableDirectory.jump]));
             }
             );
        }
    }

变量捕获 : 是指 匿名函数delegateLambda)在创建时,保存了它所使用的外部变量的引用(或值)。当回调被执行时,它会使用这些捕获的变量

捕获的效果

默认情况下,匿名函数会捕获外部变量的引用(就是地址),而不是值。这意味着如果外部变量改变了,回调函数里看到的也是最新的值。

然后就来到了我最无法理解的地方,它捕获的是一个变量的话,那在我的代码里它抓了一个已经被销毁的局部变量,按道理肯定会报错的

cs 复制代码
    private void OnButtonClicked(int jumpIndex)
    {
        dialogIndex = jumpIndex;
    }

遂问gpt,答曰:

虽然变量是局部的,但在 C# 中,捕获的变量不会被销毁,而是被延长生命周期,直到匿名函数(或者回调函数)本身不再被引用。

  • 局部变量原本的生命周期:在正常情况下,局部变量会在方法执行完毕后被销毁。
  • 匿名函数捕获变量后 :编译器会自动将局部变量"提升"到堆上,使它的生命周期与匿名函数绑定。只要匿名函数还存在,在这里就是按钮的事件监听器还在,这个变量就会被保留下来,不会被销毁。

和闭包又有什么关系呢?

闭包(Closure) 是一个编程概念

闭包是由:

  1. 函数(匿名函数或 Lambda 表达式)
  2. 被捕获的变量(函数外部的变量)

共同组成的一个"包裹",用于延长这些变量的生命周期,使得函数可以在未来某个时间访问这些变量,即使它们已经离开原来的作用域。

反正就也是一个语法吧,不受我们控制的那种。

另外区分一下c#里堆和栈的概念

栈存储内容:

  • 方法中的局部变量(如 intfloat 等值类型)。
  • 方法调用信息(包括函数参数、返回地址)。
cs 复制代码
static void Main()
{
    int number = 42; // "number" 是栈上的局部变量
    PrintNumber(number);
}

static void PrintNumber(int num)
{
    Console.WriteLine(num); // "num" 是栈上的局部变量
}

堆存储内容:

  • 通过 new 创建的对象。
  • 对象的字段(即类中的成员变量)
cs 复制代码
class Person
{
    public string Name;
}

static void Main()
{
    Person person = new Person(); // "person" 的实例分配在堆上
    person.Name = "Alice";        // "Name" 字段也存储在堆中
}

ps.person 是一个引用,存储在栈中,指向堆中的对象实例。堆上的对象由垃圾回收器管理,当没有引用指向该对象时,GC 会释放内存。

相关推荐
“抚琴”的人13 小时前
【机械视觉】C#+VisionPro联合编程———【六、visionPro连接工业相机设备】
c#·工业相机·visionpro·机械视觉
云上艺旅13 小时前
K8S学习之基础七十四:部署在线书店bookinfo
学习·云原生·容器·kubernetes
你觉得20513 小时前
哈尔滨工业大学DeepSeek公开课:探索大模型原理、技术与应用从GPT到DeepSeek|附视频与讲义下载方法
大数据·人工智能·python·gpt·学习·机器学习·aigc
A旧城以西14 小时前
数据结构(JAVA)单向,双向链表
java·开发语言·数据结构·学习·链表·intellij-idea·idea
无所谓จุ๊บ14 小时前
VTK知识学习(50)- 交互与Widget(一)
学习·vtk
FAREWELL0007514 小时前
C#核心学习(七)面向对象--封装(6)C#中的拓展方法与运算符重载: 让代码更“聪明”的魔法
学习·c#·面向对象·运算符重载·oop·拓展方法
吴梓穆15 小时前
UE5学习笔记 FPS游戏制作38 继承标准UI
笔记·学习·ue5
CodeCraft Studio15 小时前
Excel处理控件Spire.XLS系列教程:C# 合并、或取消合并 Excel 单元格
前端·c#·excel
Three~stone15 小时前
MySQL学习集--DDL
数据库·sql·学习
齐尹秦15 小时前
HTML 音频(Audio)学习笔记
学习