“Orcas”新语言特征 :Lambda (λ) 表达式
“Orcas”新语言特征:Lambda (λ) 表达式
上个月 我开始写一个关于新的VB和C#语言特征的系列,,而这些语言特性是”Orcas”版本的Visual Studio 和.NET FrameWork的组成部分。下面是这个系列的开始两个随笔。
l 自动属性,对象初始化和集合初始化
l 扩展方法
今天的博客随笔包含了另外的一个基础的新语言特征:lambda 表达式
什么是Lambda 表达式?
C#2.0(集成在VS 2005中)包含了匿名方法的概念,当期望委托值时,它将允许代码块内嵌的方式写进去。
Lambda 表达式 将提供更简明,更功能化的语法来写匿名方法。在他们提供一种简洁的,类型安全方式来写需要像参数的方式来传递并发值时,他们将最终变得异常的有用。
Lambda 表达式例子:
在我前面的扩展方法博客随笔上,我示范了你怎么样能声明一个简单的”person” 类如下:
我随后展示你怎么实例化包含值的List<Person> ,然后用Linq的新扩展方法”where” 和”Average”去返回一个集合中的人员子集,也计算了集合中的person 的平均年龄。
被红色高亮的P=>表达式和是Lambda表达式。在这个例子中,当获取people时, 我用第一个lambda表达式去指定筛选条件,在计算平均值时,我用第二个Lambda表达式来指定people对象中的值。
Lambda 表达式说明
概念性的理解Lambda表达式的最简单的方法是把他们想成写简明的内嵌方法的方式,例如,我上面写的例子可以用C# 2.0 写的匿名方法来替换,如下:
上面的两个匿名方法都用一个person 类型来作为参数。第一个匿名方法返回一个Boolean(指示person的性是否是Guthrie)。第二个匿名方法返回一个integer(返回person’s Age).我们早先用的lambda表达式,两者都用一个person 类型来作为参数. 第一个lambda 返回一个boolean,第二个Lambda返回一个integer.
在C#中,一个Lambda 表达式按照语法写成一个参数列表,随着a=> 符号,后面的表达式或者是声明块将在调用的时候被执行。
Parame=> expression
所以在我们写Lambda 表达式时
P=>p.LastName = =”Guthrie”
我们指示我们定义的Lambda表达式带一个参数 P ,并且代码表达式运行并返回是否P.LastName值等同于”Guthrie”。事实上我们命名的参数”p”是不相关的。我可以很简单的将它命名成”o”,”x”,”foo”或者其他我想命名的名称。
不像匿名方法,需要参数类型声名明确的显示出来,lambda 表达式允许参数类型省略,并且改为允许他们在应用过程中推断出来。举例,当我写Lambda表达式p=>p.LastName= =”Guthrie”,编译器推断参数 P 是一个人员类型,因为”where”扩展方法应用在一个范型列表List<Person> 集合上。
Lambda 参数类型能被编译时间和Visual Studio’s intellisense(智能感知)引擎所推断(这意味着 你在写Lambdas 时 将能被编译时间和Intellisense所感知)。举例,当我书写类型”P”时,在Visual
Studio”orcas”提供intellisense 实现的背后,是它知道P的类型是Person
注意: 假如你想明确的声明Lambda表达式中的参数类型,你可以在Lambda参数列表中在参数名前声明参数类型。
高级:为框架开发者的Lambda表达式树(Lambda Expression Trees for Framework Developers)
从框架开发者的角度来看,其中一件事能使Lambda 表达式变得异常的强大,那就是它们既可以被编译成委托代码(至于IL方法的形式) 或者是一棵Expression树对象,可以在RunTime时被用于分析,转换,优化Expression
将一个Lambda表达式编译成一棵Expression树对象的功能是非常强大的机制,它能实现一连串的场景,包含建立一个高性能的对象映射来支持丰富查询的数据(无论是关系数据库,一个Active Directory,还是Web服务)能力,使用一致的查询语言来提供编译时间的语法检查和Vs的智能感知。
Lambda 表达式到代码委托
上面的"where"扩展方法就是一个编译Lambda表达式到一个代码委托的例子(这就意味着它编译成IL是按照委托形式的调用)。Where扩展方法可以通过以下代码来实现筛选数据的功能
上面的Where扩展方法被传递了一个Fun<T,bool>类型的筛选参数,这是一个委托,带有单个T类型的参数和返回一个bool标志是否满足条件。当我们把一个Lambda表达式作为一个参数传递给这个Where()扩展的方法时,C#的编译器将会把我们的Lambda 表达式转化成一个IL方法委托(同时<T>类型将会是 person),然后我们的Where()方法将能被调用去求值是否给的条件满足。
Lambda表达式 to表达式树
当我们像我们上面的List集合一样先在内存里面赋值,那将编译Lambda表达式并转化成代码委托的工作将变得异常的巨大。但我们想象这样的场景当你需要在一个数据库中查询数据时(下面的代码是使用”orcas”中的内置的Linq到SQL 对象关系隐射)
在这里我想从数据库中提取一系列的强类型对象”Product”,并且我用Lambda表达式建立的筛选条件传给Where()扩展方法。
我不希望发生的是提取数据库中的全部Product Rows ,把它们作为对象保存到一个本地的集合众,并且用上面一样的内存中的Where()扩展方法去实现筛选。这样效率将非常低下且不适合大型数据库,相反地是,我想让LinQ to SQL ORM 将我的Lambda 筛选条件转化成SQL表达式,并在数据库中执行筛选条件。这样的方法我仅仅返回符合条件的那些行(并且一个非常有效的数据查询)。
框架开发者可以通过用Expression<T> 类型去替换Func<T>来定义它们的Lambda表达式来实现这个目标,这样会将Lambda表达式编译成一个Lambda表达式树,并且我们能组合部件和在运行时分析。
注意我用和我们早先用过的一样的p=>p.LastName == "Guthrie"Labda表达式,但是这次我分配给Expression<Func<Person, bool>>变量来替换Func<Person,bool>数据类型。比起生成IL,编译器生成一个Lambda表达式树,然后我可以像框架开发者一样分析并赋值。
在LinQ ToSQL的情景中,它将获得这个Lambda查询块,且转化成标准的关系SQL并在数据库中执行(逻辑上 "SELECT * from Products where UnitPrice < 55“)
IQueryable<T> 接口
为了帮助框架开发者建立可查询的数据供应源,Linq 运载了Iqueryable<T>接口,它实现了标准的LinQ查询操作的扩展方法并且提供了更方便的方法去实现对复杂的表达式树的处理。(举例:一些像下面的设定,我通过三个不同的扩展方法和两个Lambda表达式来实现从数据库中提取10个Products)
一些很好的系列博客帖子包含了怎样通过IQueryable<T>来建立允许LINQ的数据提供源,请从下面的链接去检验别人的这些很好的博客帖子
- LINQ to Amazon: Part 1, Part 2, Part 3
- LINQ to NHibernate: Part 1, Part 2, Part 3
- LINQ to LDAP: Part 0, Part 1, Part 2, Part 3, Part 4, Part 5
Summary
希望上述的帖子提供了一个对如何理解和使用的Lambda表达式的基础的理解。当结合在system.linq名字空间中提供的内置的标准查询扩展方法,在“ Orcas ”中,它们提供了一个真正的丰富的方法在整个编译时间和intellisense中查询和互动任何类型的数据。
通过利用表达式树来源Lambdas和IQueryable<T>接口的支持的优势,框架开发者建立数据源提供者可以确保系统开发者写得清洁的代码,快速有效的执行。(无论是a database, XML file, in-memory object, web-service, LDAP system, etc).
在接下来的数周,我会完成这项语言系列,涵盖新的核心的语言概念,从理论水平,然后向前迈进,以支付一些超级实际的例子,利用他们在行动(特别是使用linq对数据库和XML文件)。
希望这会有所帮助,
原文连接
http://weblogs.asp.net/scottgu/archive/2007/04/08/new-orcas-language-feature-lambda-expressions.aspx
posted on 2008-07-10 13:33 狗尾草-大数据收割基 阅读(1453) 评论(5) 编辑 收藏 举报