zoukankan      html  css  js  c++  java
  • js中的闭包和c#中的闭包

    我们 

    关于闭包,一个老僧长谈的话题;js的闭包俺将的比较多了,而且很详细,俺就不说了,可以看看之前的文章;

    我们来对比一下c#中的闭包和js中的闭包;

    先看我们的c#代码;

            static List<Action> fn0()
            {
                int result = 0;
                List<Action> list = new List<Action>();
                for (int i = 0; i < 10; i++)
                {
                    result = i + 1; //这样result相当于一个全局变量
                    Action action = () =>
                    {
                        Console.WriteLine(result);
                    };
                    list.Add(action);
                }
                return list;
            }
    
            static List<Action> fn1()
            {
                
                List<Action> list = new List<Action>();
                for (int i = 0; i < 10; i++)
                {
                   int result = i + 1; //这样相当于一个局部变量; for 循环内部的变量;
                    Action action = () =>
                    {
                        Console.WriteLine(result);
                    };
                    list.Add(action);
                }
                return list;
            }
    
            static void Main(string[] args)
            {
    
                //这样看到了,结果全尼玛的是十滴呀;
                fn0().ForEach(o=>o());
                Console.WriteLine("------------------"); //闭包的问题会在我们的for循环和多线程中出现id呀;效果不一般滴呀;
                fn1().ForEach(o => o());
             }

    结果可想而知;

    那么对于,第二种方式,如果我们使用js代码来实现呢;(ps:js的数组可以直接存我们的函数,ps:js中函数就是对象,所以就可以存)

    function show(){
     var arr=[];
     for(var i=0;i<10;i++){
          var result=i+1;
         arr.push(function (){
             console.log(result);
            
         })
          
      }
      return arr;
    };
    var list=show();

      for(var i=0;i<list.length;i++){
        list[i]();
      }

    
    

    结果:

     出现这样的原因很简单啦,俺都不想再重复,因为js中没有块级作用域的概念;不不不不,这样说不太正确,具体的应该是在esx版本前没有,具体的俺也记不清楚了;

    解决方法有很多呀;形成闭包啊,使用let 关键字呀;

    让我们来看看闭包是如何形成的;

    function show(){
     var arr=[];
     for(var i=0;i<10;i++){
          var result=i+1;
         arr.push(function (index){
                 return function (){
                    console.log(index);
                 }
            
         }(result))
          
      }
      return arr;
    };
    var list=show();
    for(var i=0;i<list.length;i++){
        list[i]();
    }

    它借助了我们的function(最外面的function)形成了一个新的作用域(result=index),这样i=>result=>index就形成了,我们的一个独立的块级别多用于了滴呀;

    方法还挺巧妙的,但是对于新的es,一个let就可以解决问题了;

    总结:问题的本质:js中没有块级别作用域,通过function形成一个新的额块级别作用域;这样innerfunction 访问outerfunction的变量(参数);就形成了我们的闭包;

    闭包的定义之一:这里我强调的是定义之一://函数内部可以访问函数所在的作用域;,当然它有一个明显的问题就是:内存泄漏;


    再回过头来看看我们c#中代码,以为c#中是有块级别作用域的,所以,方法二就没有问题;

    我们看看;维基百科上定义:

    闭包:词法闭包;是引用了自由变量的函数;这个被引用的自由变量,和这个函数一同存在,即是已经离开了创造它的环境;所以。有另一种说法认为:

    闭包是由函数和与其相关的引用环境组合成的实体;

    var x = 1;
    Action action = () =>
    {
     var y = 2;
     var result = x + y;
     Console.Out.WriteLine("result = {0}", result);
    };
    action();

    当你在代码调试器(debugger)里观察“action”时,会发现很有趣的事情。我们可以看到,C# 编译器为我们创建了一个 Target 类,里面封装了 x 变量:

     如最上面的栗子中,我们在fn0(for中定义第一个的result),以外的地方调用它是,变量依然是存在的;这个就是我们闭包生气的地方;

     何避免闭包陷阱呢?C#中普遍的做法是,将匿名函数引用的变量用一个临时变量保存下来,然后在匿名函数中使用临时变量

    我们再来看一段经典的js代码:

    function show(){
      
        var n=12;
        return function (){
          n++;
              console.log(n);
        }
    
    }
    
    var fuck=show();
    fuck();
    fuck();
    fuck();

    结果:

     

    key point:循环嵌套引用;

    同样的代码c#如何实现呢;

        public class Person
        {
            public Action show()
            {
                var n = 12;
                return () =>
                {
                    n++;
                    Console.WriteLine(n);
                   
                };
            }
        }
    
      public class Program{
    static void Mian(string [] args){ Person p
    = new Person(); var fuck = p.show(); fuck(); fuck(); fuck();
    }
    }

     结果:

     从上面的代码我们不难看到,变量n实际上是属于函数T1的局部变量,它本来生命周期应该是伴随着函数T1的调用结束而被释放掉的,但这里我们却在返回的委托b中仍然能调用它

    ps:

    C# 编译器帮我们做了非常多的工作,如:自动属性、类型推断、匿名类型、匿名委托、Lamda 表达式、析构方法、await 和 sync、using、对象初始化表达式、lock、默认参数 等等,

    这些东西,我们都统统称呼值为语法糖;

    这里我们再加一个链接:

    http://www.cnblogs.com/jujusharp/archive/2011/08/04/2127999.html

  • 相关阅读:
    PHP 创建二叉树镜像(交换左右子树)
    PHP 使用二叉树的先序和中序遍历结果构造二叉树
    PHP 不用求1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)
    PHP 不用加减乘除做加法
    PHP 跳台阶问题
    PHP 输入一个整数,求该整数的二进制表达中有多少个1
    PHP 输入两个整数n 和m,从数列1,2,3.......n 中随意取几个数, 使其和等于m ,要求将其中所有的可能组合列出来
    PHP 查找链表倒数第i个节点
    .Net/C# 实现: FlashFXP 地址簿中站点密码的加解密算法
    johnsuna 的收藏精品
  • 原文地址:https://www.cnblogs.com/mc67/p/8016999.html
Copyright © 2011-2022 走看看