.Net 闭包理解#

这个东西初看是比较难懂,但是一旦理解之后就很容易了,做笔记以加深印象。且看这题

  1. example.1
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. var actions = new List<Action>();
  7. for (int i = 0; i < 5; i++)
  8. {
  9. actions.Add(() =>
  10. {
  11. Print(i);
  12. });
  13. }
  14. actions.ForEach(x => x());
  15. }
  16. private static void Print(int i)
  17. {
  18. Console.WriteLine(i);
  19. }
  20. }

运行一下,结果都是5,Why?首先我们都知道,程序执行一个函数的时候,会将这个函数中用到的局部

变量压入一个栈中,退出这个函数的时候,会将栈中的数据清空。上面的代码,第一个Print函数用的i

值应该是第一个i(也就是0)所在地址的值,但是自加之后,地址变了,那么第一个Print函数用的i(也就是0)去

哪里了?我理解的话应该就是变成了一个不安全的地址,访问这个值就会出现问题。为了避免这种情况

的发生,.Net是如何处理的呢,那就是在所有Print函数执行的时候,不让这个i被清理,于是提升了i的

作用域(这个现象就叫闭包),我换个写法

  1. example.2
  2. class Program
  3. {
  4. private static int _i;
  5. static void Main(string[] args)
  6. {
  7. var actions = new List<Action>();
  8. for (_i = 0; _i < 5; _i++)
  9. {
  10. actions.Add(() =>
  11. {
  12. Print(_i);
  13. });
  14. }
  15. actions.ForEach(x => x());
  16. }
  17. private static void Print(int i)
  18. {
  19. Console.WriteLine(i);
  20. }
  21. }

_注意:_此时是不存在闭包现象的,当然也别以为.Net就是这么做的,并不是。通过IL反汇编工具查看

example.1的代码,发现它把i封装到了一个匿名的类中,可以这么想,要使一个东西在栈上不被清理,

那就把它放到堆上...,于是用了个匿名类把i当成其属性,代码如下:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. var actions = new List<Action>();
  6. var myClass = new MyClass();
  7. for (var i = 0; i < 5; i++)
  8. {
  9. myClass.i = i;
  10. actions.Add(() =>
  11. {
  12. Print(myClass);
  13. });
  14. }
  15. myClass.i++;
  16. actions.ForEach(x => x());
  17. }
  18. private static void Print(MyClass myClass)
  19. {
  20. Console.WriteLine(myClass.i);
  21. }
  22. }
  23. internal class MyClass
  24. {
  25. public int i
  26. {
  27. get; set;
  28. }
  29. }

如果我们要避免这种情况,该如何做?那就相当于上诉代码每一个循环使用一个新的MyClass

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. var actions = new List<Action>();
  6. for (var i = 0; i < 5; i++)
  7. {
  8. var myClass = new MyClass();
  9. myClass.i = i;
  10. actions.Add(() =>
  11. {
  12. Print(myClass);
  13. });
  14. }
  15. actions.ForEach(x => x());
  16. }
  17. private static void Print(MyClass myClass)
  18. {
  19. Console.WriteLine(myClass.i);
  20. }
  21. }
  22. internal class MyClass
  23. {
  24. public int i
  25. {
  26. get; set;
  27. }
  28. }

其实申明MyClass这一步也可以省了,就是如下

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. var actions = new List<Action>();
  6. for (var i = 0; i < 5; i++)
  7. {
  8. var i1 = i;
  9. actions.Add(() =>
  10. {
  11. Print(i1);
  12. });
  13. }
  14. actions.ForEach(x => x());
  15. }
  16. private static void Print(int i)
  17. {
  18. Console.WriteLine(i);
  19. }
  20. }

其实上面这个写法是Resharper提示闭包之后给出的解决方案。

以上,就是我看了别人的一些理解,再加上自己的一些理解推敲出来的东西,有许多地方是模糊的,表面的,但

闭包这东西,不理解好像也没什么影响,这里起个抛砖引玉的作用,欢迎斧正拍砖。

.Net 闭包理解的更多相关文章

  1. javascript闭包理解

    //闭包理解一 function superFun(){ var _super_a='a'; function subfuc(){ console.log(_super_a); } return su ...

  2. JavaScript中的闭包理解

    原创文章,转载请注明:JavaScript中的闭包理解  By Lucio.Yang 1.JavaScript闭包 在小学期开发项目的时候,用node.js开发了服务器,过程中遇到了node.js的第 ...

  3. javascript之闭包理解以及应用场景

    半个月没写博文了,最近一直在弄小程序,感觉也没啥好写的. 之前读了js权威指南,也写了篇博文,但是实话实说当初看闭包确实还是一头雾水.现在时隔一个多月(当然这一段时间还是一直有在看闭包的相关知识)理解 ...

  4. JavaScript闭包理解【关键字:普通函数、闭包、解决获取元素标签索引】

    以前总觉得闭包很抽象,很难理解,所以百度一下"闭包"概览,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的 ...

  5. JavaScript ——闭包理解

    昨天晚上听别人谈起闭包这个东西,虽然对js有一点了解但却丝毫没有印象,今天也没什么事就顺便研究了一下满足好奇宝宝.整合于网上的理解,记录一下. 一.闭包的作用域 要理解闭包,首先必须理解Javascr ...

  6. js闭包理解实例小结

    Js闭包 闭包前要了解的知识  1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...

  7. 对于 Javascript 闭包理解

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

  8. JS闭包理解_摘

    原文地址1:http://www.cnblogs.com/mzwr1982/archive/2012/05/20/2509295.html 闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实 ...

  9. js难点之闭包理解

    如何从外部读取局部变量? 闭包就是能够读取其他函数内部变量的函数. 由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”. 所以 ...

随机推荐

  1. 手机浏览器不支持 IDBObjectStore.getAll

    最近在学习IndexDB,使用了IDBObjectStore.getAll,发现手机上不支持. 后面,查阅了mdn:  的确是不支持,且可以看到这个函数现在兼容性很差. 解决方法: 1.使用 IDB ...

  2. Android开发4: Notification编程基础、Broadcast的使用及其静态注册、动态注册方式

    前言 啦啦啦~(博主每次开篇都要卖个萌,大家是不是都厌倦了呢~) 本篇博文希望帮助大家掌握 Broadcast 编程基础,实现动态注册 Broadcast 和静态注册 Broadcast 的方式以及学 ...

  3. SharePoint 2013 状态机工作流之日常报销示例

    简单介绍下状态机工作流,状态机工作流提供了一系列的状态.工作流从初始状态开始,到终止状态结束.两个状态之间定义行为进行过渡.通常情况下,状态机工作流对事件作出反应,事件的发生将会使状态发生改变. 1. ...

  4. IOS开发基础知识--碎片32

    1:动画属性UIViewAnimationOptions说明 a:常规动画属性设置(可以同时选择多个进行设置) UIViewAnimationOptionLayoutSubviews:动画过程中保证子 ...

  5. 在访问jsp时抛java.lang.IllegalArgumentException: Page directive: invalid value for import的原因

    问题:java.lang.IllegalArgumentException: Page directive: invalid value for import 环境:tomcat 7.0.65 出错原 ...

  6. Android View的几个位置坐标关系

    1. View的边界,left, top, right, bottom(即左上右下),这些值都是相对View的父容器说的: 2. View的x, translationX, y, translatio ...

  7. 利用split

    java.lang.string.splitsplit 方法将一个字符串分割为子字符串,然后将结果作为字符串数组返回.stringObj.split([separator,[limit]])strin ...

  8. StringUtils工具类

    StringUtils源码,使用的是commons-lang3-3.1包.下载地址 http://commons.apache.org/lang/download_lang.cgi 以下是String ...

  9. qt5.4.0编译错误

    error1: 进程"C:\Qt\Qt5.4.0\Tools\QtCreator\bin\jom.exe"退出,退出代码 2 solution:去工具->选项->构建和 ...

  10. RHEL7.2和RHEL6.5的配置网络yum源和本地yum源

    RHEL7.2配置本地yum源 [root@localhost ~]#monut /dev/sr0 /mnt      #挂载光盘 [root@localhost ~]# rm -rf /etc/yu ...