Lambda高手之路第三部分
转http://www.cnblogs.com/lazycoding/archive/2013/01/06/2847587.html
背后的秘密-MSIL
通过著名的LINQPad,我们可以更深入的查看MSIL代码而没有任何秘密。下图是一个LINQPad的使用截图
我们会看三个例子,第一个Lambda表达式如下:
- Action<string> DoSomethingLambda = (s) =>
- {
- Console.WriteLine(s);// + local
- };
对应的普通函数是这样的
- Action<string> DoSomethingLambda = (s) =>
- {
- Console.WriteLine(s);// + local
- };
生成的MSIL代码片段如下:

- DoSomethingNormal:
- IL_0000: nop
- IL_0001: ldarg.1
- IL_0002: call System.Console.WriteLine
- IL_0007: nop
- IL_0008: ret
- <Main>b__0:
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: call System.Console.WriteLine
- IL_0007: nop
- IL_0008: ret

最大的不同是方法的名称用法不同。而不是声明。事实上。声明是完全一样的。编译器在类里面创建了一个新的方法来实现这个方法。这没什么新东西,仅仅是为了我们使用Lambda表达式方便代码编写。从MSIL代码中,我们做了同样的事情。在当前对象里调用了一个方法。
我们在下图里演示一下编译器所做的修改。在这个图例。我们可以看到编译器把Lambda表达式移动成了一个固定的方法。
第二个例子将展示Lambda表达式真正的奇妙之处,在这个例子里。我们既使用了有着全局变量的普通方法也使用了有捕获变量的Lambda表达式。代码如下

- void Main()
- {
- int local = 5;
- Action<string> DoSomethingLambda = (s) => {
- Console.WriteLine(s + local);
- };
- global = local;
- DoSomethingLambda("Test 1");
- DoSomethingNormal("Test 2");
- }
- int global;
- void DoSomethingNormal(string s)
- {
- Console.WriteLine(s + global);
- }

没什么不同的似乎。关键是:lambda表达式如何被编译器处理

- IL_0000: newobj UserQuery+<>c__DisplayClass1..ctor
- IL_0005: stloc.1
- IL_0006: nop
- IL_0007: ldloc.1
- IL_0008: ldc.i4.5
- IL_0009: stfld UserQuery+<>c__DisplayClass1.local
- IL_000E: ldloc.1
- IL_000F: ldftn UserQuery+<>c__DisplayClass1.<Main>b__0
- IL_0015: newobj System.Action<System.String>..ctor
- IL_001A: stloc.0
- IL_001B: ldarg.0
- IL_001C: ldloc.1
- IL_001D: ldfld UserQuery+<>c__DisplayClass1.local
- IL_0022: stfld UserQuery.global
- IL_0027: ldloc.0
- IL_0028: ldstr "Test 1"
- IL_002D: callvirt System.Action<System.String>.Invoke
- IL_0032: nop
- IL_0033: ldarg.0
- IL_0034: ldstr "Test 2"
- IL_0039: call UserQuery.DoSomethingNormal
- IL_003E: nop
- DoSomethingNormal:
- IL_0000: nop
- IL_0001: ldarg.1
- IL_0002: ldarg.0
- IL_0003: ldfld UserQuery.global
- IL_0008: box System.Int32
- IL_000D: call System.String.Concat
- IL_0012: call System.Console.WriteLine
- IL_0017: nop
- IL_0018: ret
- <>c__DisplayClass1.<Main>b__0:
- IL_0000: nop
- IL_0001: ldarg.1
- IL_0002: ldarg.0
- IL_0003: ldfld UserQuery+<>c__DisplayClass1.local
- IL_0008: box System.Int32
- IL_000D: call System.String.Concat
- IL_0012: call System.Console.WriteLine
- IL_0017: nop
- IL_0018: ret
- <>c__DisplayClass1..ctor:
- IL_0000: ldarg.0
- IL_0001: call System.Object..ctor
- IL_0006: ret

和第一个例子一样。机制相同。编译器把lambda表达式移动到一个方法里。但是不同的是,编译器这次还生成了一个类。编译器为我们的lambda 表达式生成的方法会放在类里,这就给了捕获的变量一个全局的作用域,通过这样。Lambda表达式可以访问局部变量。因为在MSIL里。它是类实例里面的 一个全局变量。
所有的变量因此就可以在新生成的类的对象里赋值/读取了。这解决了变量之间的引用问题。(其实就是只保留了对该类实例的引用。)编译器也足够智能之 会把这些捕获的变量放到类里面。因此,当我们使用Lambda的时候才没有太大的性能问题。无论如何。注意。由于保持了对lambda表达式的引用,因此 可能造成内存泄漏。只要方法还在。变量就仍然存活。显而易见。而现在我们知道了原因。
我们再次用图示来说明。这这种闭包情况下里。不仅仅方法会被移动。捕获的变量也会被移动。所有被移动了的对象会被放到一个新生成的类里。因此一个没有名称的类就隐式的出现了。
Lambda高手之路第三部分的更多相关文章
- Python高手之路【三】python基础之函数
基本数据类型补充: set 是一个无序且不重复的元素集合 class set(object): """ set() -> new empty set object ...
- Lambda高手之路第二部分
转http://www.cnblogs.com/lazycoding/archive/2013/01/06/2847579.html 闭包的影响 为了展示闭包的影响,我们看下面这个例子. var bu ...
- Lambda高手之路第一部分
转http://www.cnblogs.com/lazycoding/archive/2013/01/06/2847574.html 介绍 Lambda表达式是使代码更加动态,易于扩展并且更加快速(看 ...
- [js高手之路] html5 canvas系列教程 - arcTo(弧度与二次,三次贝塞尔曲线以及在线工具)
之前,我写了一个arc函数的用法:[js高手之路] html5 canvas系列教程 - arc绘制曲线图形(曲线,弧线,圆形). arcTo: cxt.arcTo( cx, cy, x2, y2, ...
- 云计算分布式大数据Hadoop实战高手之路第七讲Hadoop图文训练课程:通过HDFS的心跳来测试replication具体的工作机制和流程
这一讲主要深入使用HDFS命令行工具操作Hadoop分布式集群,主要是通过实验的配置hdfs-site.xml文件的心跳来测试replication具体的工作和流程. 通过HDFS的心跳来测试repl ...
- 云计算分布式大数据Hadoop实战高手之路第八讲Hadoop图文训练课程:Hadoop文件系统的操作实战
本讲通过实验的方式讲解Hadoop文件系统的操作. “云计算分布式大数据Hadoop实战高手之路”之完整发布目录 云计算分布式大数据实战技术Hadoop交流群:312494188,每天都会在群中发布云 ...
- [js高手之路] es6系列教程 - 对象功能扩展详解
第一:字面量对象的方法,支持缩写形式 //es6之前,这么写 var User = { name : 'ghostwu', showName : function(){ return this.nam ...
- [js高手之路]从原型链开始图解继承到组合继承的产生
基于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情 一.把父类的实例对象赋给子类的原型对象(prototype),可以实现继承 f ...
- [js高手之路]原型对象(prototype)与原型链相关属性与方法详解
一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手 ...
随机推荐
- 17.1 Replication Configuration 复制:
17.1 Replication Configuration 复制: 17.1.1 How to Set Up Replication 17.1.2 Replication Formats 17.1. ...
- RAID级别与规范
1.RAID 0 RAID 0是最早出现的RAID模式,即Data Stripping数据分条技术.RAID 0是组建磁盘阵列中最简单的一种形式,只需要2块以上的硬盘即可,成本低,可以提高整个磁盘的性 ...
- UVALive 6931 Can't stop playing (Regionals 2014 >> Europe - Central)
题目 一开始有一个双头队列,每次添加一个数(这是数是二的幂,所有数的和不大于\(2^13\)),由你来决定添加到队头还是队尾.如果队列里面相邻的两个数相同,设它们都是\(x\),那么这两个数会合并为\ ...
- 怎样获取浏览器上次的会话数据(session)
怎样获取浏览器上次的会话数据: 要知道上次会话session_id是多少? 让cookie保存的PHPSESSID的值不要浏览器一退出就被删除了? 所以我们要设置自己的session_id,不要系统自 ...
- poll调用深入解析
poll调用深入解析http://blog.csdn.net/zmxiangde_88/article/details/8099049 poll调用和select调用实现的功能一样,都是网络IO利用的 ...
- java BigDecimal的使用和四舍五入及格式规范(精准数据)
• Java中的简单浮点数类型float和double不能够进行运算.不光是Java,在其它很多编程语言中也有这样的问题. 如果我们编译运行下面这个程序会看到什么? public class T ...
- 面试中关于Java中涉及到知识点(转)
本篇文章会对面试中常遇到的Java技术点进行全面深入的总结,帮助我们在面试中更加得心应手,不参加面试的同学也能够借此机会梳理一下自己的知识体系,进行查漏补缺. 1. Java中的原始数据类型都有哪些, ...
- Can't call commit when autocommit=true(转)
java.sql.SQLException: Can't call commit when autocommit=true at com.mysql.jdbc.SQLError.createSQLEx ...
- 用Eclipse做Android开发时出现java.lang.NoClassDefFoundError问题
之前有遇到过这个问题,后来解决了,今天又遇到了,但是忘了当时是怎么解决的,费了好长时间,终于又找回解决的方法,现在记录下来,以防以后又遇到. 这个错误出现在我的某一个Activity,但是我反复确认了 ...
- PHP 的解压缩ZipArchive中的extractTo()方法 LINUX+nginx环境中解压zip时文件丢失的问题
在项目中要用ZipArchive解压ZIP文件,起初測试环境在WINDOWS平台中,測试通过,换到 LINUX+nginx 的环境中时 就出问题了(ZIP包中有文件和目录一共3百多个文件,大部分是带汉 ...