[C# 基础知识系列]专题十四:深入理解Lambda表达式
引言:
对于刚刚接触Lambda表达式的朋友们,可能会对Lambda表达式感到非常疑惑,它到底是个什么什么样的技术呢?以及它有什么好处和先进的地方呢?下面的介绍将会解除你这些疑惑。
一、Lambda表达式的演变过程
Lambda表达式其实大家可以理解为它是一个匿名函数(对于匿名函数的介绍大家可以参考我这篇文章), Lambda表达式可以包含表达式和语句,并且可以用于创建委托,以及C#编译器也能将它转换成表达式树。
对于Lambda表达式中都会使用这个运算符——“=>”,它读成“goes to” ,该运算符的左边为输入参数,右边是表达式或者语句块,下面就看看Lambda表达式是如何来创建委托实例(代码同时也给出了Lambda表达式从匿名方法的演示过程,从而帮助大家更好的理解Lambda表达式是匿名函数的概念,只不过C#3 中提出的Lambda表达式比匿名函数的使用更加简洁和直观了,其实原理都是一样的, 编译器同样会把Lambda表达式编译成匿名函数,也就是一个名字的方法):
- using System;
- namespace Lambda表达式Demo
- {
- class Program
- {
- /// <summary>
- /// Lambda 表达式使用演示
- /// </summary>
- /// <param name="args"></param>
- static void Main(string[] args)
- {
- // Lambda表达式的演变过程
- // 下面是C# 1中创建委托实例的代码
- Func<string, int> delegatetest1 = new Func<string, int>(Callbackmethod);
- // ↓
- // C# 2中用匿名方法来创建委托实例,此时就不需要额外定义回调方法Callbackmethod
- Func<string, int> delegatetest2 = delegate(string text)
- {
- return text.Length;
- };
- // ↓
- // C# 3中使用Lambda表达式来创建委托实例
- Func<string, int> delegatetest3 = (string text) => text.Length;
- // ↓
- // 可以省略参数类型string,把上面代码再简化为:
- Func<string, int> delegatetest4 = (text) => text.Length;
- // ↓
- // 如果Lambda表达式只需一个参数,并且那个参数可以隐式指定类型时,
- // 此时可以把圆括号也省略,简化为:
- Func<string, int> delegatetest = text => text.Length;
- // 调用委托
- Console.WriteLine("使用Lambda表达式返回字符串的长度为: " + delegatetest("learning hard"));
- Console.Read();
- }
- /// <summary>
- /// 回调方法
- /// 如果使用了Lambda表达式和匿名函数,此方法就不需要额外定义
- /// </summary>
- /// <param name="text"></param>
- /// <returns></returns>
- private static int Callbackmethod(string text)
- {
- return text.Length;
- }
- }
- }
运行结果为:
上面代码中都有详细的演变过程,这里就不多解释了,希望通过这部分之后,大家可以对Lambda表达式有进一步的理解,其实Lambda表达式就是匿名方法,其中使用Lambda表达式来创建委托实例,我们却没有指出创建的委托类型,其中编译器会帮助我们去推断委托类型,从而简化我们创建委托类型所需要的代码,从而更加简洁,所以Lambda表达式可以总结为——它是在匿名方法的基础上,再进一步地简化了创建委托实例所需要的代码。
二、Lambda表达式的使用
为了帮助大家更好的理解Lambda表达式,下面演示下用Lambda表达式来记录事件(代码中Lambda运算符的右边调用了一个回调方法ReportEvent()):
- using System;
- using System.Windows.Forms;
- namespace Lambda表达式来记录事件Demo
- {
- class Program
- {
- static void Main(string[] args)
- {
- // 新建一个button实例
- Button button1 = new Button() { Text ="点击我"};
- // C# 2中使用匿名方法来订阅事件
- //button1.Click+=delegate (object sender,EventArgs e)
- //{
- // ReportEvent("Click事件", sender, e);
- //};
- //button1.KeyPress += delegate (object sender, KeyPressEventArgs e)
- //{
- // ReportEvent("KeyPress事件,即键盘按下事件", sender, e);
- //};
- // C# 3Lambda表达式方式来订阅事件
- // 与上面使用匿名方法来订阅事件是不是看出简单了很多,并且也直观了
- button1.Click += (sender, e) => ReportEvent("Click事件", sender, e);
- button1.KeyPress += (sender, e) => ReportEvent("KeyPress事件,即键盘按下事件", sender, e);
- // C# 3之前初始化对象时使用下面代码
- //Form form = new Form();
- //form.Name = "在控制台中创建的窗体";
- //form.AutoSize = true;
- //form.Controls.Add(button1);
- // C# 3中使用对象初始化器
- // 与上面代码的比较中,也可以看出使用对象初始化之后代码简化了很多
- Form form = new Form { Name = "在控制台中创建的窗体", AutoSize = true, Controls = { button1 } };
- // 运行窗体
- Application.Run(form);
- }
- // 记录事件的回调方法
- private static void ReportEvent(string title, object sender, EventArgs e)
- {
- Console.WriteLine("发生的事件为:{0}", title);
- Console.WriteLine("发生事件的对象为:{0}", sender);
- Console.WriteLine("发生事件参数为: {0}", e.GetType());
- Console.WriteLine();
- Console.WriteLine();
- }
- }
- }
运行结果:
从上面代码中可以看出,使用Lambda表达式之后代码确实简洁了很多,上面代码中都有详细的注释,这里就不解释了,大家可以查看代码中的注释来进行理解,并且代码中注释部分也列出了C# 3之前是如何实现这样的代码的, 这样有利于比较,从而帮助大家更好的认识到Lambda所带来的好处和进一步来理解Lambda表达式。
三、表达式树
上面指出Lambda表达式除了可以用来创建委托外,C#编译器还可以将他们转换成表达式树——用于表示Lambda表达式逻辑的一种数据结构,表达式树也可以称作表达式目录树,它将代码表示成一个对象树,而不是可执行的代码。对于刚接触哦表达式树的朋友肯定会问——为什么需要把Lambda表达式转化为表达式目录树呢?对于表达式树的提出主要是为后面Linq to SQL 做铺垫,一个Linq to SQL 的查询语句并不是在C#的程序中执行的,而是C#编译器把它转化为SQL 语句,然后再在数据库中执行。在我们使用Linq to SQL的时候都需要添加一个Linq to SQL的类,该类的扩展名dbml,该的作用就是帮助我们把Linq to SQL 的语句映射为SQL语句,然后再在数据库中执行SQL语句,把返回的结果再返回给一个IQueryable集合,所以Linq to SQL 也采用了通常的ORM(Object—Relationship—Mapping)来设计的,相当于是一个ORM框架,不过这个框架只能与微软的SQL server数据库进行映射,对于其他类型的数据库却不可以,然而很多其他开发人员却对此进行了一些扩展,扩展了对其他数据库的支持。前不久还在博客园中发布了开源的Linq框架的,名字为ELinq,其他它就是对Linq to SQL的一个扩展,使Linq语句可以映射到其他数据库的查询语句。
下面先看看如何把Lambda表达式转化为表达式目录树(其中需要引入一个新的命名空间—— System.Linq.Expressions):
- using System;
- // 引用额外的命名空间
- using System.Linq.Expressions;
- namespace 表达式树Demo
- {
- class Program
- {
- /// <summary>
- /// 表达式树的演示
- /// </summary>
- /// <param name="args"></param>
- static void Main(string[] args)
- {
- #region 将Lambda表达式转换为表达式树演示
- // 将Lambda表达式转换为Express<T>的表达式树
- // 此时express不是可执行的代码,它现在是一个表达式树的数据结构
- Console.WriteLine("将Lambda表达式转化为表达式树的演示:");
- Expression<Func<int, int, int>> expression = (a, b) => a + b;
- // 获得表达式树的参数
- Console.WriteLine("参数1: {0},参数2:{1}", expression.Parameters[0],expression.Parameters[1]);
- // 既然叫做树,那肯定有左右节点
- // 获取表达式树的主体部分
- BinaryExpression body = (BinaryExpression)expression.Body;
- // 左节点,每个节点本身就是一个表达式对象
- ParameterExpression left = (ParameterExpression)body.Left;
- // 右节点
- ParameterExpression right = (ParameterExpression)body.Right;
- Console.WriteLine("表达式主体为:");
- Console.WriteLine(expression.Body);
- Console.WriteLine("表达式树左节点为:{0}{4} 节点类型为:{1}{4}{4} 表达式右节点为:{2}{4} 节点类型为:{3}{4}", left.Name, left.NodeType, right.Name, right.NodeType,Environment.NewLine);
- Console.Read();
- #endregion
- #region 把表达式树转化回可执行代码
- // Compile方法生成Lambda表达式的委托
- Console.WriteLine("按下Enter键进入将表达式树转换为Lambda表达式的委托演示:");
- int result = expression.Compile()(2, 3);
- Console.WriteLine("调用Lambda表达式委托结果为:" + result);
- Console.ReadKey();
- #endregion
- }
- }
- }
运行结果:
上面代码首先把Lambda表达式转化为表达式树,下面这行代码就是把Lambda表达式转化为表达式树:
- Expression<Func<int, int, int>> expression = (a, b) => a + b;
之后对于表达式树这种数据结构进行分析来获得该树中的主体和左右节点是什么,获得主体和左右节点的代码如下:
- // 获取表达式树的主体部分
- BinaryExpression body = (BinaryExpression)expression.Body;
- // 左节点,每个节点本身就是一个表达式对象
- ParameterExpression left = (ParameterExpression)body.Left;
- // 右节点
- ParameterExpression right = (ParameterExpression)body.Right;
从上面代码可以得出——树中的每个节点都是一个表达式(ParameterExpression和BinaryExpression都是继承Expression的,所以左右节点都是表达式),分析完表达式树之后,代码中还演示了如果把表达式树转化为可执行的代码,即转化为Lambda表达式的委托对象(此时调用Expression的Compile()方法来转化为可执行代码),通过调用委托来获得结果。
关于Lambda表达式树的更多信息还可以参看这篇博客:http://www.cnblogs.com/tianfan/archive/2010/03/05/expression-tree-basics.html (博主翻译的还可以)
四、总结
到这里本专题的内容也介绍的差不多了,希望通过本专题使一些之前对Lambda表达式感到疑惑的朋友们现在可以理解Lambda表达式,因为只有理解好Lambda表达式之后,对于Linq的学习就可以说是轻而易举了。
附件:http://down.51cto.com/data/2361951
本文转自LearningHard 51CTO博客,原文链接:http://blog.51cto.com/learninghard/1087512,如需转载请自行联系原作者
[C# 基础知识系列]专题十四:深入理解Lambda表达式相关推荐
- [C#基础知识系列]专题十七:深入理解动态类型
本专题概要: 动态类型介绍 为什么需要动态类型 动态类型的使用 动态类型背后的故事 动态类型的约束 实现动态行为 总结 引言: 终于迎来了我们C# 4中特性了,C# 4主要有两方面的改善--Com 互 ...
- [C#基础知识系列]专题十:全面解析可空类型
引言: C# 2.0 中还引入了可空类型,可空类型也是值类型,只是可空类型是包括null的值类型的,下面就介绍下C#2.0中对可空类型的支持具体有哪些内容(最近一直都在思考如何来分享这篇文章的,因为刚 ...
- [C#基础知识系列]专题十二:迭代器
引言: 在C# 1.0中我们经常使用foreach来遍历一个集合中的元素,然而一个类型要能够使用foreach关键字来对其进行遍历必须实现IEnumerable或IEnumerable<T> ...
- [C# 基础知识系列]专题十五:全面解析扩展方法
引言: C# 3中所有特性的提出都是更好地为Linq服务的, 充分理解这些基础特性后.对于更深层次地去理解Linq的架构方面会更加简单,从而就可以自己去实现一个简单的ORM框架的,对于Linq的学习 ...
- [C#基础知识系列]专题十:全面解析可空类型[转]
原文链接 主要内容: 1:空合并操作符(?? 操作符) ??操作符也就是"空合并操作符",它代表的意思是两个操作数,如果左边的数不为null时,就返回左边的数,如果左边的数为nul ...
- 视口锁定解锁lisp_CAD基础知识汇总第十四期:如何创建异形视口和解除视口锁定?...
视口的问题在CAD设计中也是一个常常困扰设计师的一个问题,今天的CAD基础知识汇总就教大家如何创建异形视口和解除视口锁定. 一.如何创建异形视口,创建非矩形视口 解决方法一 在命令行输入[MVIEW] ...
- 链方法[C# 基础知识系列]专题三:如何用委托包装多个方法——委托链
最近研究链方法,稍微总结一下,以后继续补充: 弁言: 上一专题分析了下编译器是如何来翻译委托的,从中间语言的角度去看委托,希望可以帮助大家进一步的理解委托,然而之前的分析都是委托只是封装一个方法,那委 ...
- 【转】[C# 基础知识系列]专题四:事件揭秘
引言: 前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听到"事件"这个概念的,尤其是写UI的时候,当我们点击一个按钮后VS就会自动帮我们生成一些后台的代码,然 ...
- 【问链财经-区块链基础知识系列】 第四十五课 一文读懂保理业务的操作流程
国内保理业务,是指销售商将其现在或将来的基于其与购货商(债务人)订立的货物销售与服务合同或因其他原因所产生的应收账款转让给我行,从而获得我行为其提供的商业资信调查.融资.应收账款管理等方面的综合性金融 ...
最新文章
- java中窗体背景图片_Java Swing 之设置窗体背景图片
- 基于.NetCore3.1系列 —— 日志记录之自定义日志组件
- hash的算法 java_【数据结构与算法】一致性Hash算法及Java实践
- java jcsh执行linux命令,java jcsh执行linux命令
- 计算机基础ABCDEF,计算机应用基础_在线作业ABCDEF.docx
- 质量故事(2)---降落伞的真实故事
- java隔一个逗号读入文件_将逗号分隔的文本文件读入HashMap,其中文件在多行Java中具有相同的键...
- html动态资源加载进度,JavaScript_快速解决js动态改变dom元素属性后页面及时渲染的问题,今天实现一个进度条加载过程 - phpStudy...
- 快速创建精彩的Flash游戏(一) Flash2D游戏引擎简介
- mysql5.7.23分区表_MySQL5.7.23 VS MySQL5.6.21 分区表性能对比测试
- 将excel表数据顺序与linux,Excel中表格数据进行颠倒顺序的设置方法
- myexcel导入导出数据
- 239期夏天计算机开机号,福彩3D16239期便民工作室提供中国福彩中心开机号239期开机号...
- 离散数学 数学三大危机
- Promise基础知识
- java合并单元格边框不完整,java poi 合并单元格后边框问题
- 看泽塔云如何布局自己的超融合之路
- vue使用过滤器,文字超出显示省略号
- 高性能 MySQL实战
- 学编程买什么类型的电脑适合?从预算到配置,给你安排的明明白白!
热门文章
- 解决无法使用locate命令的方法
- 有些文档,本来想整理整理贴出来,
- 二叉树的各种操作(转)
- Kafka源码分析-序列3 -Producer -Java NIO(Reactor VS Peactor)
- 压力测试以及编译安装httpd2.4
- 广州运营开放式数据交易平台发力大数据业务
- [译]使用Webpack提高Vue.js应用程序的4种方式
- Sqli-labs less 40
- 【Java并发性和多线程】Java中的锁
- [转]Sublime Text 2 C++编译运行简单配置