转http://www.cnblogs.com/lazycoding/archive/2013/01/06/2847587.html

背后的秘密-MSIL

通过著名的LINQPad,我们可以更深入的查看MSIL代码而没有任何秘密。下图是一个LINQPad的使用截图

我们会看三个例子,第一个Lambda表达式如下:

  1. Action<string> DoSomethingLambda = (s) =>
  2. {
  3. Console.WriteLine(s);// + local
  4. };

对应的普通函数是这样的

  1. Action<string> DoSomethingLambda = (s) =>
  2. {
  3. Console.WriteLine(s);// + local
  4. };

生成的MSIL代码片段如下:

  1. DoSomethingNormal:
  2. IL_0000: nop
  3. IL_0001: ldarg.1
  4. IL_0002: call System.Console.WriteLine
  5. IL_0007: nop
  6. IL_0008: ret
  7. <Main>b__0:
  8. IL_0000: nop
  9. IL_0001: ldarg.0
  10. IL_0002: call System.Console.WriteLine
  11. IL_0007: nop
  12. IL_0008: ret

最大的不同是方法的名称用法不同。而不是声明。事实上。声明是完全一样的。编译器在类里面创建了一个新的方法来实现这个方法。这没什么新东西,仅仅是为了我们使用Lambda表达式方便代码编写。从MSIL代码中,我们做了同样的事情。在当前对象里调用了一个方法。

我们在下图里演示一下编译器所做的修改。在这个图例。我们可以看到编译器把Lambda表达式移动成了一个固定的方法。


第二个例子将展示Lambda表达式真正的奇妙之处,在这个例子里。我们既使用了有着全局变量的普通方法也使用了有捕获变量的Lambda表达式。代码如下

  1. void Main()
  2. {
  3. int local = 5;
  4.  
  5. Action<string> DoSomethingLambda = (s) => {
  6. Console.WriteLine(s + local);
  7. };
  8.  
  9. global = local;
  10.  
  11. DoSomethingLambda("Test 1");
  12. DoSomethingNormal("Test 2");
  13. }
  14.  
  15. int global;
  16.  
  17. void DoSomethingNormal(string s)
  18. {
  19. Console.WriteLine(s + global);
  20. }

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

  1. IL_0000: newobj UserQuery+<>c__DisplayClass1..ctor
  2. IL_0005: stloc.1
  3. IL_0006: nop
  4. IL_0007: ldloc.1
  5. IL_0008: ldc.i4.5
  6. IL_0009: stfld UserQuery+<>c__DisplayClass1.local
  7. IL_000E: ldloc.1
  8. IL_000F: ldftn UserQuery+<>c__DisplayClass1.<Main>b__0
  9. IL_0015: newobj System.Action<System.String>..ctor
  10. IL_001A: stloc.0
  11. IL_001B: ldarg.0
  12. IL_001C: ldloc.1
  13. IL_001D: ldfld UserQuery+<>c__DisplayClass1.local
  14. IL_0022: stfld UserQuery.global
  15. IL_0027: ldloc.0
  16. IL_0028: ldstr "Test 1"
  17. IL_002D: callvirt System.Action<System.String>.Invoke
  18. IL_0032: nop
  19. IL_0033: ldarg.0
  20. IL_0034: ldstr "Test 2"
  21. IL_0039: call UserQuery.DoSomethingNormal
  22. IL_003E: nop
  23.  
  24. DoSomethingNormal:
  25. IL_0000: nop
  26. IL_0001: ldarg.1
  27. IL_0002: ldarg.0
  28. IL_0003: ldfld UserQuery.global
  29. IL_0008: box System.Int32
  30. IL_000D: call System.String.Concat
  31. IL_0012: call System.Console.WriteLine
  32. IL_0017: nop
  33. IL_0018: ret
  34.  
  35. <>c__DisplayClass1.<Main>b__0:
  36. IL_0000: nop
  37. IL_0001: ldarg.1
  38. IL_0002: ldarg.0
  39. IL_0003: ldfld UserQuery+<>c__DisplayClass1.local
  40. IL_0008: box System.Int32
  41. IL_000D: call System.String.Concat
  42. IL_0012: call System.Console.WriteLine
  43. IL_0017: nop
  44. IL_0018: ret
  45.  
  46. <>c__DisplayClass1..ctor:
  47. IL_0000: ldarg.0
  48. IL_0001: call System.Object..ctor
  49. IL_0006: ret

和第一个例子一样。机制相同。编译器把lambda表达式移动到一个方法里。但是不同的是,编译器这次还生成了一个类。编译器为我们的lambda 表达式生成的方法会放在类里,这就给了捕获的变量一个全局的作用域,通过这样。Lambda表达式可以访问局部变量。因为在MSIL里。它是类实例里面的 一个全局变量。

所有的变量因此就可以在新生成的类的对象里赋值/读取了。这解决了变量之间的引用问题。(其实就是只保留了对该类实例的引用。)编译器也足够智能之 会把这些捕获的变量放到类里面。因此,当我们使用Lambda的时候才没有太大的性能问题。无论如何。注意。由于保持了对lambda表达式的引用,因此 可能造成内存泄漏。只要方法还在。变量就仍然存活。显而易见。而现在我们知道了原因。

我们再次用图示来说明。这这种闭包情况下里。不仅仅方法会被移动。捕获的变量也会被移动。所有被移动了的对象会被放到一个新生成的类里。因此一个没有名称的类就隐式的出现了。

Lambda高手之路第三部分的更多相关文章

  1. Python高手之路【三】python基础之函数

    基本数据类型补充: set 是一个无序且不重复的元素集合 class set(object): """ set() -> new empty set object ...

  2. Lambda高手之路第二部分

    转http://www.cnblogs.com/lazycoding/archive/2013/01/06/2847579.html 闭包的影响 为了展示闭包的影响,我们看下面这个例子. var bu ...

  3. Lambda高手之路第一部分

    转http://www.cnblogs.com/lazycoding/archive/2013/01/06/2847574.html 介绍 Lambda表达式是使代码更加动态,易于扩展并且更加快速(看 ...

  4. [js高手之路] html5 canvas系列教程 - arcTo(弧度与二次,三次贝塞尔曲线以及在线工具)

    之前,我写了一个arc函数的用法:[js高手之路] html5 canvas系列教程 - arc绘制曲线图形(曲线,弧线,圆形). arcTo: cxt.arcTo( cx, cy, x2, y2, ...

  5. 云计算分布式大数据Hadoop实战高手之路第七讲Hadoop图文训练课程:通过HDFS的心跳来测试replication具体的工作机制和流程

    这一讲主要深入使用HDFS命令行工具操作Hadoop分布式集群,主要是通过实验的配置hdfs-site.xml文件的心跳来测试replication具体的工作和流程. 通过HDFS的心跳来测试repl ...

  6. 云计算分布式大数据Hadoop实战高手之路第八讲Hadoop图文训练课程:Hadoop文件系统的操作实战

    本讲通过实验的方式讲解Hadoop文件系统的操作. “云计算分布式大数据Hadoop实战高手之路”之完整发布目录 云计算分布式大数据实战技术Hadoop交流群:312494188,每天都会在群中发布云 ...

  7. [js高手之路] es6系列教程 - 对象功能扩展详解

    第一:字面量对象的方法,支持缩写形式 //es6之前,这么写 var User = { name : 'ghostwu', showName : function(){ return this.nam ...

  8. [js高手之路]从原型链开始图解继承到组合继承的产生

    基于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情 一.把父类的实例对象赋给子类的原型对象(prototype),可以实现继承 f ...

  9. [js高手之路]原型对象(prototype)与原型链相关属性与方法详解

    一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手 ...

随机推荐

  1. 17.1 Replication Configuration 复制:

    17.1 Replication Configuration 复制: 17.1.1 How to Set Up Replication 17.1.2 Replication Formats 17.1. ...

  2. RAID级别与规范

    1.RAID 0 RAID 0是最早出现的RAID模式,即Data Stripping数据分条技术.RAID 0是组建磁盘阵列中最简单的一种形式,只需要2块以上的硬盘即可,成本低,可以提高整个磁盘的性 ...

  3. UVALive 6931 Can't stop playing (Regionals 2014 >> Europe - Central)

    题目 一开始有一个双头队列,每次添加一个数(这是数是二的幂,所有数的和不大于\(2^13\)),由你来决定添加到队头还是队尾.如果队列里面相邻的两个数相同,设它们都是\(x\),那么这两个数会合并为\ ...

  4. 怎样获取浏览器上次的会话数据(session)

    怎样获取浏览器上次的会话数据: 要知道上次会话session_id是多少? 让cookie保存的PHPSESSID的值不要浏览器一退出就被删除了? 所以我们要设置自己的session_id,不要系统自 ...

  5. poll调用深入解析

    poll调用深入解析http://blog.csdn.net/zmxiangde_88/article/details/8099049 poll调用和select调用实现的功能一样,都是网络IO利用的 ...

  6. java BigDecimal的使用和四舍五入及格式规范(精准数据)

    • Java中的简单浮点数类型float和double不能够进行运算.不光是Java,在其它很多编程语言中也有这样的问题. 如果我们编译运行下面这个程序会看到什么? public   class  T ...

  7. 面试中关于Java中涉及到知识点(转)

    本篇文章会对面试中常遇到的Java技术点进行全面深入的总结,帮助我们在面试中更加得心应手,不参加面试的同学也能够借此机会梳理一下自己的知识体系,进行查漏补缺. 1. Java中的原始数据类型都有哪些, ...

  8. Can't call commit when autocommit=true(转)

    java.sql.SQLException: Can't call commit when autocommit=true at com.mysql.jdbc.SQLError.createSQLEx ...

  9. 用Eclipse做Android开发时出现java.lang.NoClassDefFoundError问题

    之前有遇到过这个问题,后来解决了,今天又遇到了,但是忘了当时是怎么解决的,费了好长时间,终于又找回解决的方法,现在记录下来,以防以后又遇到. 这个错误出现在我的某一个Activity,但是我反复确认了 ...

  10. PHP 的解压缩ZipArchive中的extractTo()方法 LINUX+nginx环境中解压zip时文件丢失的问题

    在项目中要用ZipArchive解压ZIP文件,起初測试环境在WINDOWS平台中,測试通过,换到 LINUX+nginx 的环境中时 就出问题了(ZIP包中有文件和目录一共3百多个文件,大部分是带汉 ...