[原译]Lambda高手之路第二部分
闭包的影响
为了展示闭包的影响,我们看下面这个例子。
var buttons = new Button[10];for(var i = 0; i < buttons.Length; i++) {var button = new Button();button.Text = (i + 1) + ". Button - Click for Index!";button.OnClick += (s, e) => { Messagebox.Show(i.ToString()); };buttons[i] = button; } //如果我们点击按钮会发生什么
这个问题很怪,我在我的JavaScript课程上经常问我的学生。95%的学生会说。显然按钮0显示0,按钮1显示1,等等。而不足5%的学生学习了闭包之后会明白。所有的按钮都会显示10.
局部变量i的值改变了。并且等于buttons.Length。也就是10了。想要避免这个诡异的情况也很简单。如下就行了。
var button = new Button(); var index = i; button.Text = (i + 1) + ". Button - Click for Index!"; button.OnClick += (s, e) => { Messagebox.Show(index.ToString()); }; buttons[i] = button;
问题解决了,但是index变量是一个值类型,因此保留了“全局”i的一个拷贝
最后一个话题是一个叫做表达式树的东西,他和Lambda表达式协作。并且,他会使得在ASP.NET MVC中的Html扩展方法发生一些很奇妙的东西。关键的问题是:如何发现目标函数
1. 传递进去的变量的名称是什么
2. 方法的主体是什么
3. 函数体里使用了什么类型
Expression 解决了这些问题,他允许我们挖掘生成的表达式树,我们也可以执行传递给Func和Action委托的函数,而且,可以在运行时解析Lambda表达式
我们看一下如何使用Expression 类型的例子
Expression<Func<MyModel, int>> expr = model => model.MyProperty; var member = expr.Body as MemberExpression; var propertyName = memberExpression.Member.Name; //当 member != null的时候执行 ...
这是Expression最简单的用法了。规则也相当直接。通过构建一个Expression类型的对象。编译器为生成的解释树生成一些元数据。这个解释树包含所有相关的信息,比如参数和方法体。
方法体包含完整的解释树,我们可以访问这些操作。就像完整的语句一样。也可以操作返回指和类型。当然,返回可以为null,无论如此。大多数情况下。我们对表达式很感兴趣。这和ASP.NET MVC中处理Expression类型的方法是很相似的—可以得到使用的参数的名字,而好处是显而易见的。不会出现名称拼写错误了。也就不会出现因此而出现的编译错误了。
注意:当程序员仅仅对调用的属性的名字感兴趣的时候。有一个更简单,更优雅的解决方案,那就是参数特性CallerMemberName 这个特性可以得到调用方法/属性的名称。而字段被编译器自动填充。因此,如果我们仅仅想知道名字,而不想知道其他更多的信息,那么我我们只需要写出像下面这个例子里的代码就行了。通过调用WhatsMyName() 方法可以返回方法的名字
string WhatsMyName([CallerMemberName] string callingName = null) {return callingName; }
Lambda表达式的性能
有一个很大的问题:Lambda表达式有多快?好吧。首先,我们期望他们能和我们一般的函数一样快。下一节里。我们会看到Lambda的MSIL代码和普通的函数没有太大的不同。
最有趣的一个讨论是如果Lambda表达式产生了闭包,将会和使用全局变量的方法一样快。这就产生了一个有趣的问题,和一个区域内局部变量的数目多少会有关系吗?
我们看看测试代码,通过4个不同的指标,我们可以看到普通方法和Lambda方法的不同。
using System; using System.Collections.Generic; using System.Diagnostics;namespace LambdaTests {class StandardBenchmark : Benchmark{const int LENGTH = 100000;static double[] A;static double[] B;static void Init(){var r = new Random();A = new double[LENGTH];B = new double[LENGTH];for (var i = 0; i < LENGTH; i++){A[i] = r.NextDouble();B[i] = r.NextDouble();}}static long LambdaBenchmark(){Func<double> Perform = () =>{var sum = 0.0;for (var i = 0; i < LENGTH; i++)sum += A[i] * B[i];return sum;};var iterations = new double[100];var timing = new Stopwatch();timing.Start();for (var j = 0; j < iterations.Length; j++)iterations[j] = Perform();timing.Stop();Console.WriteLine("Time for Lambda-Benchmark: \t {0}ms", timing.ElapsedMilliseconds);return timing.ElapsedMilliseconds;}static long NormalBenchmark(){var iterations = new double[100];var timing = new Stopwatch();timing.Start();for (var j = 0; j < iterations.Length; j++)iterations[j] = NormalPerform();timing.Stop();Console.WriteLine("Time for Normal-Benchmark: \t {0}ms", timing.ElapsedMilliseconds);return timing.ElapsedMilliseconds;}static double NormalPerform(){var sum = 0.0;for (var i = 0; i < LENGTH; i++)sum += A[i] * B[i];return sum;}} }
本来用Lambda表达式我们可以把代码写的更好。最终我没有这样做。是为了防止影响看最后的结果。因此,本质上。上述代码里有三个方法
1个是Lambda测试,一个是普通测试,还有一个是在普通测试里调用的方法。而没有的第四个方法其实是我们的lambda表达式。在第一个方法里已经创建了。计算过程很简单,我们随机取数字防止编译器做任何优化。最后我们对普通方法和Lambda方法的不同很有兴趣。
如果我们运行这个测试程序。我们会发现Lambda表达式并不总是比普通方法差。我们惊奇的是有时候甚至Lambda表达式更快一些。然而,在有闭包的情况下,,就不对了。这就是说大多数情况下我们可以毫不犹豫的使用lambda表达式。当使用闭包的时候会出现一点点的性能损失,不过还好,下一节会讲述性能损失的几个原因。
下面是测试输出的结果表
Test Lambda [ms] Normal [ms]
0 45+-1 46+-1
1 44+-1 46+-2
2 49+-3 45+-2
3 48+-2 45+-2
下图是上表的结果,我们可以看得到普通的方法和Lambda表达式基本上限差不多。当使用Lambda表达式的时候没有太大的性能损失。
第三部分会介绍背后的秘密-MSIL
转载于:https://www.cnblogs.com/lazycoding/archive/2013/01/06/2847579.html
[原译]Lambda高手之路第二部分相关推荐
- Hbase高手之路 -- 第二章 -- HBase集群的搭建
Hbase高手之路 – 第二章 – HBase集群的搭建 一. 下载并安装 1. 下载安装包 2. 上传服务器 3. 解压 tar -zxvf hbase-2.4.10-bin.tar.gz -C . ...
- Python编程高手之路——第二章:流程控制
二.流程控制 2.1 运算方式 计算机的核心部件是CPU,CPU有两个功能,控制和运算: 2.1.1 数学运算 print(a ** b) # 幂 print(a // b) // 取整数 2.1.2 ...
- 安卓高手之路 图形系统(2)----------------基于Binder的架构思路)
在学习安卓的时候最迷惑的就是Binder.图形框架的理解与Binder的理解分不开.前面一篇 [ 安卓高手之路之java层Binder 从代码角度分析了Java层Binder的实现原理.在C++层,这 ...
- 安卓高手之路之 ClassLoader
我不喜欢那些泛泛而谈的去讲那些形而上学的道理,更不喜欢记那些既定的东西.靠记忆去弥补思考的人,容易陷入人云亦云的境地,最后必定被记忆所围困,而最终消亡的是创造力.希望这个高手之路系列能够记录我学习安卓 ...
- [js高手之路] html5 canvas系列教程 - 掌握画直线图形的常用API
我们接着上文[js高手之路] html5 canvas系列教程 - 认识canvas以及基本使用方法继续. 一.直线的绘制 cxt.moveTo( x1, y1 ): 将画笔移动到x1, y1这个点 ...
- 《Python高手之路(第3版)》——1.5 Joshua Harlow访谈
本节书摘来自异步社区<Python高手之路(第3版)>一书中的第1章,第1.5节,作者[法]Julien Danjou,王飞龙 译,更多章节内容可以访问云栖社区"异步社区&quo ...
- 【免费直播】零基础Office速通,助您走向Word/Excel/PPT高手之路
1 关于本门课程 [课程费用] 免费 [讲师介绍] 老宋,MOS讲师,2010年获得WORD和EXCEL 2003版专家级认证,2018年获得OFFICE 2016版大师级认证,OFFICE多年实 ...
- JavaScript高手之路:构造函数方式封装对象
本节的主要内容是讲解new Object方式创建对象以及以这种方式来封装几个有趣的类.我们先来回顾一下new Object是怎么创建对象的,再说一下啊new Object方式创建对象如何演变. new ...
- [js高手之路]设计模式系列课程-发布者,订阅者重构购物车
发布者订阅者模式,是一种很常见的模式,比如: 一.买卖房子 生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色 中介拿到卖主的房 ...
- 安卓高手之路 图形系统(4 Measure的算法)
安卓高手之路 图形系统(4 Measure的算法) - 修补C++ - ITeye技术网站 Java代码 /** * Does the hard part of measureChildren: ...
最新文章
- OC 的反射机制以及使用场景
- php 5.4.36 sqlserver,PHP:PHP5.4连接SQLSERVER
- 网站外部链接优化如何进一步提升?
- try/catch/finally的简单实践
- animation of android (1)
- ARM嵌入式系统malloc的实现(C源码)
- http://www.tldp.org/LDP/abs/abs-guide.txt.gz
- 春季:@Component与@Bean
- Copula函数理论及实现(三维)-MATLAB
- 阿里图标库的基本使用
- 改写jtopo滚轮缩放代码
- 三菱工控板底层源码_三菱PLC实例代码开源PLC项目源代码参考程序百度云资源下载...
- 计算机房电脑装软件,机房轻松批量安装软件
- 夏普Android系统,夏普LCD-60\70TX85A及YunOS安卓运行环境安装指南
- Js一句话实现打开QQ和客服聊天
- Python绘制TSP、VRP问题求解结果图
- oracle bpm 表单,Oracle BPM 安全认证
- android个人记账软件(附上源码)
- 第二章 STC51开发环境搭建
- DevExpress GridControl 控件中GridView 加组,加行
热门文章
- TFS小记(3):建立团队项目
- Mac基础知识:在mac上怎么使用程序坞
- NoteBurner iTunes DRM Audio Converter for Mac(苹果DRM音频转换器)
- iOS底层探索之KVO(五)—FBKVOController分析
- Google 正式发布 Fuchsia OS,Flutter 集成尚存问题
- 使用EasyRecovery轻松修复损坏的照片
- [Swift][leetcode] 433. 最小基因变化
- Seoer,牵起用户与搜索引擎双手的魔术师
- TCP/IP 域名系统DNS
- Android应用开发基础篇(13)-----GestureDetector(手势识别)