[点滴系列][1]:从闭包说起

  世界杯车轮战开始了,连通三天,基本进入世界杯状态。看球也不能忘了玩技术,这次打算把接触c#以来的点滴总结起来,让原本模糊的概念清晰起来,博友们一起来吧!

  [闭包]接触这个词的第一感觉就是晦涩难懂,下面我们就来啃一啃。

一、邂逅[闭包]

  第一次接触闭包是在js里,先来看代码段[1]:

1 function a() {
2     var i = 0;
3     function b() {
4         alert(++i);
5     }
6     return b;
7 }
8 var c = a();
9 c(); 

js的闭包

  很简单的代码,细心观察会发现变量i的作用域是在方法a中,也就是说出了方法a后变量i就不起作用了,可代码中的i依然活跃在方法c中,是不是违背了程序的基本规则呢?球出界了队员还在踢。

二、直面[闭包]

  先来啃啃[闭包]的正经概念,[闭包(Closure)]就是引用了自由变量的表达式,通常是函数(也就是代码段[1]中的函数b)。它的特点是被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外,通俗的讲就是大家常说的闭包延长了变量的生命期。对照代码段[1],清晰些了吗?

  下面来看c#版的,代码段[2]:

 1     public class Program
 2     {
 3         public static Action funcB()
 4         {
 5             Console.WriteLine("funcB Begin..");
 6             int i = 0;
 7             i++;
 8             Console.WriteLine("funcB:" + i);
 9             Action action = () =>
10             {
11                 Console.WriteLine("funcA:" + i);
12             };
13             i = 100;
14             Console.WriteLine("funcB:" + i);
15             Console.WriteLine("funcB End..");
16             return action;
17         }
18         static void Main()
19         {
20             var action = funcB();
21             action();
22             Console.ReadKey();
23         }
24     }

c#的闭包 写法1

 1     public class Program
 2     {
 3         public static async void funcA(Action callback)
 4         {
 5             //停留5秒,缓下节奏
 6             await Task.Delay(5000);
 7             Console.WriteLine("funcA continue..");
 8             callback();
 9         }
10         public static void funcB()
11         {
12             Console.WriteLine("funcB Begin..");
13             int i = 0;
14             i++;
15             Console.WriteLine("funcB:" + i);
16             funcA(() =>
17             {
18                 Console.WriteLine("funcA:" + i);
19             });
20             i = 100;
21             Console.WriteLine("funcB:" + i);
22         }
23         static void Main()
24         {
25             funcB();
26             Console.WriteLine("funcB End..");
27             Console.ReadKey();
28         }
29     }

c#的闭包 写法2

  两个写法目的一样,就是想勾起大家的所有疑问。代码段[2]的运行结果是什么呢?为什么是这样的结果呢?[闭包]真的延长了变量的生命期吗?相信熟悉的人是清楚的,我们要做的是继续深挖。

三、深挖[闭包]

  我们都懂这只是语法糖,它并没有违背程序的基本规律。下面就抄家伙(.NET Reflector)来窥窥究竟。

                  

图[1]                                                                                     图[2]

图[3]

图[4]

图[5]

一下上了5张图,不要慌,慢慢来。

图[1]是Reflector中Program类的字段、方法、类等的名称列表。我们注意到,除去我们代码中定义的,编译器还自动生成了一个类:c__DisplayClass1 !!

图[2]是编译器自动生成的类c__DisplayClass1中的一个方法<funcB>b__0的定义,其实就是funcB方法中的那个匿名函数 ()=>{Console.WriteLine("funcA:" + i);} 的实现;

图[3]是编译器自动生成的类c__DisplayClass1的实现的IL代码,请注意方法<funcB>b__0和公共变量i

图[4]、图[5]是funB方法的IL代码,每一段代表的啥意思我都大概做了标注。可以看到:在方法的一开始,编译器就初始化了c__DisplayClass1类的一个实例,之后对于变量i的操作,在IL中其实就是对于起初初始化的那个全局的c__DisplayClass1类实例中的变量i的操作,所以说[闭包]延长了变量的生命期是假象,其实我们一直在操作一个全局的类实例的变量。

四、模仿[闭包]

  原理基本清楚了,下面我们来自己动手模仿一下编译器做的事。

  代码段[3]:

 1     public class Program
 2     {
 3         //定义一个全局的c__DisplayClass1类型的变量。
 4         static c__DisplayClass1 displayCls;
 5         /// <summary>
 6         /// 这就是类似于编译器的那个自定义类<>c__DisplayClass1
 7         /// </summary>
 8         sealed class c__DisplayClass1
 9         {
10             public int i;
11
12             public void b_0()
13             {
14                 Console.WriteLine("funcA:" + i);
15             }
16         }
17         public static Action funcB()
18         {
19             displayCls = new c__DisplayClass1();
20             Console.WriteLine("funcB Begin..");
21             displayCls.i = 0;
22             displayCls.i++;
23             Console.WriteLine("funcB:" + displayCls.i);
24             Action action = displayCls.b_0;
25             displayCls.i = 100;
26             Console.WriteLine("funcB:" + displayCls.i);
27             Console.WriteLine("funcB End..");
28             return action;
29         }
30         static void Main()
31         {
32             var action = funcB();
33             action();
34             Console.ReadKey();
35         }
36     }

类似闭包

  编译器费尽心思给我们做了一个语法糖,让我们的编程更加轻松优雅。

五、终极想象

  只上代码,代码段[4]:

 1     public class Program
 2     {
 3         public static List<Action> funcB()
 4         {
 5             List<Action> list = new List<Action>();
 6             Console.WriteLine("funcB Begin..");
 7             int i = 0;
 8             i++;
 9             Console.WriteLine("funcB:" + i);
10             Action action1 = () =>
11             {
12                 Console.WriteLine("funcA:" + i);
13                 i = 200;
14             };
15             Action action2 = () =>
16             {
17                Console.WriteLine("funcA:" + i);
18             };
19             i = 100;
20             Console.WriteLine("funcB:" + i);
21             Console.WriteLine("funcB End..");
22             list.Add(action1);
23             list.Add(action2);
24             return list;
25         }
26         static void Main()
27         {
28             var action = funcB();
29             action[0]();
30             action[1]();
31             Console.ReadKey();
32         }
33     }

终极测试

  运行结果是什么呢?自己动手,丰衣足食。

  就到这儿吧,有问题的地方希望各位指正,敬礼!

  

[点滴系列][1]:从闭包说起

时间: 2024-08-24 21:49:53

[点滴系列][1]:从闭包说起的相关文章

JavaScript系列----作用域链和闭包

1.作用域链 1.1.什么是作用域 谈起作用域链,我们就不得不从作用域开始谈起.因为所谓的作用域链就是由多个作用域组成的.那么, 什么是作用域呢? 1.1.1作用域是一个函数在执行时期的执行环境. 每一个函数在执行的时候都有着其特有的执行环境,ECMAScript标准规定,在javascript中只有函数才拥有作用域.换句话,也就是说,JS中不存在块级作用域.比如下面这样: function getA() { if (false) { var a = 1; } console.log(a); /

委托、Lambda表达式、事件系列05,Action委托与闭包

来看使用Action委托的一个实例: static void Main(string[] args) { int i = 0; Action a = () => i++; a(); a(); Console.WriteLine(i); } 结果是期望能的2.但令人好奇的是:栈上的变量i是如何传递给Action委托的? 反编译进行查看,首先看Main方法对应的IL代码: 再看c_DisplayClass1的IL代码: 从中可以看出:→在托管堆上创建了一个名为c_DisplayClass1的实例→把

【深入理解javascript原型和闭包系列 】 历时半月完稿,求推荐

从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然不至于上头条,但是也算是中规中矩,有看的人,也有评论的人.特别是在后期讲闭包的时候. 我从来都不做基础入门教程,因为基础入门的教程,要想讲的和别人不一样很难.所以,基础入门的教程网上有的是,大家随便搜索就是了,再大不了就花钱买本书看看.而想原型和闭包这类的稍微高级一些的教程,仔细想想,还真的有的讲,真的能讲出自己的思路. 我觉得只要是自己埋头要做的

深入理解JavaScript系列(16):闭包(Closures)

介绍 本章我们将介绍在JavaScript里大家常常来讨论的话题 -- 闭包(closure).闭包事实上大家都已经谈烂了.虽然如此,这里还是要试着从理论角度来讨论下闭包,看看ECMAScript中的闭包内部到底是怎样工作的. 正如在前面的文章中提到的.这些文章都是系列文章,相互之间都是有关联的.因此.为了更好的理解本文要介绍的内容,建议先去阅读第14章作用域链和第12章变量对象. 英文原文:http://dmitrysoshnikov.com/ecmascript/chapter-6-clos

JavaScript内部原理系列-闭包(Closures)

原文链接:http://www.faceye.net/search/142545.html 概要 本文将介绍一个在JavaScript经常会拿来讨论的话题 —— 闭包(closure).闭包其实已经是个老生常谈的话题了: 有大量文章都介绍过闭包的内容(其中不失一些很好的文章,比如,扩展阅读中Richard Cornford的文章就非常好), 尽管如此,这里还是要试着从理论角度来讨论下闭包,看看ECMAScript中的闭包内部究竟是如何工作的. 正如在此前文章中提到的,这些文章都是系列文章,相互之

JavaScript 闭包系列二 --- 匿名函数及函数的闭包

一. 匿名函数 1. 函数的定义,可分为三种 1) 函数声明方式 function double(x) {    return 2*x;} 2)Function构造函数,把参数列表和函数体都作为字符串,不方便,不建议使用 var double = new Function('x', 'return 2*x;'); 3)函数表达式方式 var double = function(x) {    return 2*x;} 该形式中,等号右边是一个匿名函数,创建函数完毕后,将该函数赋给了变量doubl

深入理解javascript原型和闭包系列

从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然不至于上头条,但是也算是中规中矩,有看的人,也有评论的人.特别是在后期讲闭包的时候. 我从来都不做基础入门教程,因为基础入门的教程,要想讲的和别人不一样很难.所以,基础入门的教程网上有的是,大家随便搜索就是了,再大不了就花钱买本书看看.而想原型和闭包这类的稍微高级一些的教程,仔细想想,还真的有的讲,真的能讲出自己的思路. 我觉得只要是自己埋头要做的

Scala 系列(十)—— 函数 &amp; 闭包 &amp; 柯里化

一.函数 1.1 函数与方法 Scala 中函数与方法的区别非常小,如果函数作为某个对象的成员,这样的函数被称为方法,否则就是一个正常的函数. // 定义方法 def multi1(x:Int) = {x * x} // 定义函数 val multi2 = (x: Int) => {x * x} println(multi1(3)) //输出 9 println(multi2(3)) //输出 9 也可以使用 def 定义函数: def multi3 = (x: Int) => {x * x}

javascript系列2 -- 闭包详解

转发请标明来源:http://www.cnblogs.com/johnhou/p/javascript.html  请尊重笔者的劳动成果  --John Hou 今天我们从内存结构上来讲解下 javascript中的闭包概念. 闭包:是指有权访问另外一个函数作用域中的变量的函数.创建闭包的常见方式就是在一个函数内部创建另外一个函数. 在javascript中没有块级作用域,一般为了给某个函数申明一些只有该函数才能使用的局部变量时,我们就会用到闭包,这样我们可以很大程度上减少全局作用域中的变量,净