背景

C# 在编译器层面为我们提供了闭包机制(Java7 和 Go 也是这种思路),本文简单的做个解释。

背景知识

你必须了解:引用类型、值类型、引用、对象、值类型的值(简称值)。

关于引用、对象和值在内存的分配有如下几点规则:

  • 对象分配在堆中。
  • 作为字段的引用分配在堆中(内嵌在对象中)。
  • 作为局部变量(参数也是具备变量)的引用分配在栈中。
  • 作为字段的值分配在堆中(内嵌在对象中)。
  • 作为局部变量(参数也是具备变量)的值用分配在栈中。
  • 局部变量只能存活于所在的作用域(方法中的大括号确定了作用域的长短)。

注:按值传递和按引用传递也是需要掌握的知识点,C# 默认是按值传递的。

闭包示例

测试代码

 1         private static void Before()
2 {
3 Action[] actions = new Action[10];
4
5 for (var i = 0; i < actions.Length; i++)
6 {
7 actions[i] = () =>
8 {
9 Console.WriteLine(i);
10 };
11 }
12
13 foreach (var item in actions)
14 {
15 item();
16 }
17 }

输出结果

编译器帮我们做了是什么?

编译器帮我们生成的代码(我自己写的,可以使用 Reflector 工具自己查看)

 1         private static void After()
2 {
3 Action[] actions = new Action[10];
4
5 var anonymous = new AnonymousClass();
6
7 for (anonymous.i = 0; anonymous.i < actions.Length; anonymous.i++)
8 {
9 actions[anonymous.i ] = anonymous.Action;
10 }
11
12 foreach (var item in actions)
13 {
14 item();
15 }
16 }
17
18 class AnonymousClass
19 {
20 public int i;
21
22 public void Action()
23 {
24 Console.WriteLine(this.i);
25 }
26 }

如何修复上面的问题?

上面的例子不是我们期望的输出,让我们给出两种修改方案:

第一种(借鉴JS)

 1         private static void Fix()
2 {
3 Action[] actions = new Action[10];
4
5 for (var i = 0; i < actions.Length; i++)
6 {
7 new Action<int>((j) =>
8 {
9 actions[i] = () =>
10 {
11 Console.WriteLine(j);
12 };
13 })(i);
14 }
15
16 foreach (var item in actions)
17 {
18 item();
19 }
20 }

第二种

 1         public static void Fix2()
2 {
3 Action[] actions = new Action[10];
4
5 for (var i = 0; i < actions.Length; i++)
6 {
7 var j = i;
8
9 actions[i] = () =>
10 {
11 Console.WriteLine(j);
12 };
13 }
14
15 foreach (var item in actions)
16 {
17 item();
18 }
19 }

分析

编译器将闭包引用的局部变量转换为匿名类型的字段,导致了局部变量分配在堆中。

备注

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

 
分类: .NET

C#编译器闭包机制的更多相关文章

  1. JavaScript——闭包机制

    闭包机制是JavaScript的重点和难点,本文希望能帮助大家轻松的学习闭包 一.什么是闭包? 闭包就是可以访问另一个函数作用域中变量的函数.下面列举出常见的闭包实现方式,以例子讲解闭包概念 func ...

  2. 以python为例讲解闭包机制

    以python为例讲解闭包机制 缘起 在学习JS的过程中,总是无可避免的接触到闭包机制,尤其是接触到react后,其函数式的编程思想更是将闭包发扬光大,作为函数式编程的重要语法结构,python自然也 ...

  3. C++编译器模板机制剖析

    思考:为什么函数模板可以和函数重载放在一块.C++编译器是如何提供函数模板机制的? 一.编译器编译原理 什么是gcc gcc(GNU C Compiler)编译器的作者是Richard Stallma ...

  4. Javascript闭包机制(转)

    原文地址:http://www.felixwoo.com/archives/247 参考:http://www.ruanyifeng.com/blog/2009/08/learning_javascr ...

  5. C#学习笔记----C#中的闭包机制

    http://www.cnblogs.com/jiejie_peng/p/3701070.html http://www.cnblogs.com/Ribbon/p/3611457.html “ 若匿名 ...

  6. JS闭包机制实现为DOM元素循环添加事件

    HTML代码: <button type='button' class='btn' id='1'>按钮1</button> <button type='button' c ...

  7. 探索c#之函数创建和闭包

    阅读目录: 动态创建函数 匿名函数不足之处 理解c#中的闭包 闭包的优点 动态创建函数 大多数同学,都或多或少的使用过.回顾下c#中动态创建函数的进化: C# 1.0中: public delegat ...

  8. C#函数式程序设计之用闭包封装数据

    如果一个程序设计语言能够用高阶函数解决问题,则意味着数据作用域问题已十分突出.当函数可以当成参数和返回值在函数之间进行传递时,编译器利用闭包扩展变量的作用域,以保证随时能得到所需要的数据. C#函数式 ...

  9. c#之函数创建和闭包

    c#之函数创建和闭包 阅读目录: 动态创建函数 匿名函数不足之处 理解c#中的闭包 闭包的优点 动态创建函数 大多数同学,都或多或少的使用过.回顾下c#中动态创建函数的进化: C# 1.0中: pub ...

随机推荐

  1. java字节中的基本类型的职业的数目 (采访总是问)

    因为移动装置存储器中的移动开发的局限性,数据的字节数需要考虑往往在占领中使用的类型. 下面介绍下一个Java,以加深记忆. 在Java中一共同拥有8种基本数据类型,当中有4种整型,2种浮点类型,1种用 ...

  2. 重写ArcGIS的TiledMapServiceLayer呼叫世界地图图块

    require(["esri/layers/TiledMapServiceLayer"], function () {     dojo.declare("com.Str ...

  3. order by使用方法

    1.ORDER BY 中关于NULL的处理 缺省处理,Oracle在Order by 时觉得null是最大值,所以假设是ASC升序则排在最后,DESC降序则排在最前. 当然,你也能够使用nulls f ...

  4. obj-c编程15[Cocoa实例03]:MVC以及归档化演示样例

    前面的博文里介绍了归档和解档,这里我们把它实际应用到一个简单的代码中去,将它作为一个多文档应用程序的打开和保存的背后支持.另外这里介绍一下MVC思想,这个在不论什么语言里都会有,它是一种设计思想,主要 ...

  5. [CLR via C#]5.2 引用类型和值类型

    原文:[CLR via C#]5.2 引用类型和值类型 CLR支持两种类型:引用类型和值类型. 虽然FCL中大多数都是引用类型,但开发人员用的最多的还是值类型.引用类型总是在托管堆上分配的,C#的ne ...

  6. c# p2p 穿透(源码加密)

    http://blog.oraycn.com/ESFramework_Demo_P2P.aspx 测试,完全OK!  我很喜欢这个.可以源码是加密的!我希望实现 web 版本的p2p视频观看,aehy ...

  7. asp.net mvc3 数据验证(三)—自定义数据注解

    原文:asp.net mvc3 数据验证(三)-自定义数据注解         前两节讲的都是asp.net mvc3预先设定的数据注解,但是系统自由的数据注解肯定不适合所有的场合,所以有时候我们需要 ...

  8. WinForm播放视频

    原文:WinForm播放视频 1背景 这几天一老友要求我做个小软件,在WinForm播放视频.印象中微软有个WM控件直接可以使用,晚上研究下 2实现方式 2.1微软草根 最简单的方式,是直接使用微软的 ...

  9. 安卓MonkeyRunner源码分析之启动

    在工作中因为要追求完成目标的效率,所以更多是强调实战,注重招式,关注怎么去用各种框架来实现目的.但是如果一味只是注重招式,缺少对原理这个内功的了解,相信自己很难对各种框架有更深入的理解. 从几个月前开 ...

  10. 思维方式--SMART原则

    假设你的项目管理.系统架构的兴趣,请加微信订阅号"softjg",收藏此PM.建筑师的家 万事开头于你目标的设定,假设開始走错了,那么后面的路将会更加的错误.甚至于更加的努力犯错就 ...