zoukankan      html  css  js  c++  java
  • [点滴系列][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 Task 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     }
    终极测试

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

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

      

  • 相关阅读:
    zbb20181207 springboot @ConfigurationProperties使用
    zbb20181206 logback,lombok 默认日志logback配置解析
    Spring Boot (8) 全局异常处理
    Spring Boot (7) JdbcTemplate访问数据库
    Spring Boot (6) Spring Data JPA
    Spring Boot (4) 静态页面和Thymeleaf模板
    Spring Boot (3) 热部署devtools
    Spring Boot (2) Restful风格接口
    Spring Boot (1) 构建第一个Spring Boot工程
    idea使用maven搭建ssm框架实现登陆商品增删改查
  • 原文地址:https://www.cnblogs.com/such/p/2600976.html
Copyright © 2011-2022 走看看