转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高手之路第三部分的更多相关文章

  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. 清华集训2014 day1 task3 奇数国

    题目 题目看起来好像很难的样子!其实不然,这是最简单的一道题. 算法 首先要注意的是: \(number \cdot x + product \cdot y = 1\) ,那么我们称\(number\ ...

  2. HDU2571:命运(DP)

    Problem Description 穿过幽谷意味着离大魔王lemon已经无限接近了! 可谁能想到,yifenfei在斩杀了一些虾兵蟹将后,却再次面临命运大迷宫的考验,这是魔王lemon设下的又一个 ...

  3. 怎样从 Google Play 下载 Android 程序到电脑上

    想必非常多朋友也有须要通过电脑下载Google Play的apk到电脑端的时候,事实上非常easy,推荐一个站点:APK Downloader APK Downloader 是一个能直接从网页下载Go ...

  4. mysql学习之中的一个:mysql安装

    我用的时mac系统,本来想在mac系统上装一个,可是发现mac系统始终无法用password登入到本机server,很奇怪的问题(在stackflow上看了些回复,也没有找到原因),最后仅仅好装到虚拟 ...

  5. Swift - 后台获取数据(Background Fetch)的实现

    前面讲了如何让程序申请后台短时运行.但这个额外延长的时间毕竟有限.所以从iOS7起又引入两种在后台运行任务的方式:后台获取和后台通知. 1,后台获取介绍 后台获取(Background Fetch)是 ...

  6. Swift - 使用NSURLSession加载数据、下载、上传文件

    NSURLSession类支持三种类型的任务:加载数据.下载和上传.下面通过样例分别进行介绍. 1,使用Data Task加载数据 使用全局的sharedSession()和dataTaskWithR ...

  7. 【linux】内核源代码下载与阅读

      原创,转载时请注明,谢谢.邮箱:tangzhongp@163.com 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http://blog. ...

  8. XML 关键字

    SGML--Standard Generalized Marked Language 标准通用标记语言GML--Generalized Marked Language 通用标记语言XML--Extes ...

  9. 临界段CCriticalSection的使用

    类CCriticalSection的对象表示一个“临界区”,它是一个用于同步的对象,同一时刻仅仅同意一个线程存取资源或代码区.临界区在控制一次仅仅有一个线程改动数据或其他的控制资源时很实用.比如,在链 ...

  10. OpenStreetMap初探(一)——了解OpenStreetMap

    1. 開始关注OpenStreetMap始于此博文:<微软对抗谷歌的秘密武器:开源地图OpenStreetMap>  http://news.csdn.net/a/20120328/313 ...