Products
GG网络技术分享 2025-03-18 16:14 3
闭包的概念:
内层的函数可以引用包含在它外层的函数的变量,即使外层函数的执行已经终止。但该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。
要理解闭包,首先要搞懂清楚是变量的作用域和生命周期。我们以C#为例:
在C#中,变量作用域有三种,一种是属于类的,称之为field;第二属于函数的,我们通常称之为局部变量;还有一种,也是属于函数的,不过它的作用范围更小,它只属于函数局部的代码片段,这种同样称之为局部变量。 这三种变量的生命周期基本都可以用一句话来说明,每个变量都属于它所寄存的对象,即变量随着其寄存对象生而生和消亡。对应三种作用域我们可以这样说,类里面的变量是随着类的实例化而生,同时伴随着类对象的资源回收而消亡(当然这里不包括非实例化的static和const对象)。而函数(或代码片段)的变量也随着函数(或代码片段)调用开始而生,伴随函数(或代码片段)调用结束而自动由GC释放,它内部变量生命周期满足先进后出的特性。在作用域以外不能对变量进行读写等操作。
作用域外试图去操作变量时,提示当前上下文不存在XXX等类似的错误提示。
那么这里有没有例外呢? 答案是有的
先来看一段代码:
变量n实际上是属于函数T1的局部变量,它本来生命周期应该是伴随着函数T1的调用结束而被释放掉的,但这里我们却在返回的委托b中仍然能调用它,因为T1调用返回的匿名委托的代码片段中我们用到了n,而在编译器看来,这些都是合法的,因为返回的委托b和函数T1存在上下文关系,也就是说匿名委托b是允许使用它所在的函数或者类里面的局部变量的,于是编译器通过一系列动作(具体动作我们后面再说)使b中调用的函数T1的局部变量自动闭合,从而使该局部变量满足新的作用范围。
闭包的优点:
使用闭包,我们可以轻松的访问外层函数定义的变量,这在匿名方法中普遍使用。比如有如下场景,在winform应用程序中,我们希望做这么一个效果,当用户关闭窗体时,给用户一个提示框。我们将添加如下代码:
如果我们不使用匿名函数,就必须用其他方式来把tipWords的值传递给FormClosing注册的处理函数,这就增加了不必要的代码工作量。所以说闭包可以极大的简化我们的代码工作量,使我们的代码更加优美简洁。
闭包的陷阱:
应用闭包,我们要注意一个陷阱。比如有一个学生信息的数组,我们需要遍历每一个用户,对各个用户做处理后输出用户名。
首先建立一个学生类,包含学生姓名和年龄
然后在主函数里声明一组学生数组,当然我是在winform里面的按钮click事件注册的函数里写的,你也可以在别的地方。
预想的输出应该为:”张三”,”李四”,”王五”。
但是实际运行中会报错:提示索引超出界限。
为什么没有达到我们预期的效果呢?让我们再来看一下闭包的概念。内层函数引用的外层函数的变量时,该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。就是说,当线程中执行方法时,方法中的i参数的值,并不是从0累加到2,而是始终是累加道德极限值,也就是3。原来如此,那我们应该如何避免这种陷阱呢?
C#中普遍的做法是,将匿名函数引用的变量用一个临时变量保存下来,然后在匿名函数中使用临时变量。
我们再运行来看,输出依次为 ”张三”,”李四”,”王五”。.注意,每次的输出顺序可能不同,这是由于此处的线程执行顺序是由CPU调度的。
闭包并不是针对某一特定语言的概念,而是一个通用的概念。除了在各个支持函数式编程的语言中,我们会接触到它。一些不支持函数式编程的语言中也能支持闭包(如java8之前的匿名内部类)。
函数外部无法访问函数内部的局部变量,但函数内部的函数可以访问本函数内的局部变量,故通过闭包实现函数外部访问函数内部局部变量。但容易造成内存泄漏,应当谨慎使用。
闭包的使用场景:
1】、采用函数引用方式的setTimeout调用
setTimeout的第一个参数一般是一个即将要执行的函数,第二个参数是一个延迟时间。
如果一段代码想要通过setTimeout来调用,那么它需要传递一个函数对象的引用来作为第一个参数,但这个函数对象的引用无法为将要被延迟执行的对象提供参数。此时可以调用另一个函数来返回一个内部函数的调用,将那个内部函数对象的引用传递给setTimeout函数,内部函数执行时需要的参数,在调用外部函数时传递给它,setTimeout在执行内部函数时无需传递参数,因为内部函数仍然能够防伪外部函数调用时提供的参数。
2】、小范围代替全局变量
3】、访问私有变量的特权方法?
特权方法:有权访问私有变量和私有函数的公有方法
私有变量包括:
1)、局部变量
2)、函数的参数
3)、函数内部定义的其他函数(闭包)
以上代码的构造函数中定义了两个特权方法:getName()、setName(),这两个方法可以通过对象访问,而且都有权访问私有变量name,但是在Person构造函数外部,没有任何方法可以访问name。由于这两个方法是在函数内部定义的,因此作为闭包能够通过作用域链访问到name
以上就是闭包应用场景有哪些的详细内容,更多请关注网站的其它相关文章!
Demand feedback