c#3.0新特性(二):Lambda表达式

简介:
一、由一个简单的排序想到的
在javascript中,数组对象Array有一个sort方法,在 javascript:内置对象学习笔记二 我已经整理总结了一下,现在拿出来重温一遍:
Code
也可以这么写:
Code
js的排序简单直观,在c#里,也可以实现类似的排序,而且更强大和方便,比如c#中的泛型排序方法,常见的List<T>就有如下Sort方法:
public   void  Sort();
public   void  Sort(Comparison < T >  comparison);
public   void  Sort(IComparer < T >  comparer);
public   void  Sort( int  index,  int  count, IComparer < T >  comparer);
现以List<T>举一个应用实例:
Code
我们还可以用下面的匿名方法代替上面的BookComparasion类的方法(虽然我不喜欢匿名方法):
Code
下面的代码可以轻松实现相同的功能:
Code
上面的代码注释已经写的很清楚,没错,现在向“主流”靠拢,就是Lambda表达式了。
二、Lambda表达式学习笔记
在C#2.0中引入了匿名方法允许在期望出现委托的时候以“内联”的代码替代之。尽管匿名方法提供了函数式编程语言中的很多表达能力,但匿名方法的语法实在是太“不近人情”了,并且很不自然,用多了会造成阅读上的困难。举例来说:
Code
上面的匿名委托我们可以简化为Lambda表达式来表示。 实际上Lambda表达式的本质是匿名方法,也即是当编译我们的程序代码时,编译器会自动帮我们将Lambda表达式转换为匿名方法。看下改进后的代码:
下面系统学习Lambda表达式的相关语法和应用:
1、 创建
Lambda表达式的书写方式是一个参数列表后跟“=>”记号,然后跟一个表达式或一个语句块,即Lambda表达式的语法格式为:参数列 => 语句或语句块。
复制代码
(param1, param2, …paramN)  =>
{
statement1;
statement2;

statementN;
return (lambda_expression_return_type);

复制代码
2、关于“ 参数列

Lambda表达式的参数列可以具有显式的或隐式的类型。在一个具有显式类型的参数列表中,每个参数的类型都是显式声明的。在一个具有隐式类型的参数列表中,参数的类型是从Lambda表达式出现的上下文中推断出来的——具体来说,是当Lambda表达式被转换为一个兼容的委托类型时,该委托类型提供了参数的类型。
当Lambda表达式只有一个具有隐式类型的参数时,参数列表中的括号可以省略。即:(param) => expression可以简写为:param => expression。
注意,参数列中可包含任意个参数(与委托对应),如果参数列中有0个或1个以上参数,则必须使用括号括住参数列,举例如下:
() => Console.Write("0个参数");

i => Console.Write("1个参数时参数列中可省略括号,值为:{0}", i);

(x, y) => Console.Write("包含2个参数,值为:{0}:{1}", x, y);

而“语句或语句块”中如果只有一条语句,则可以不用大括号括住,否则则必须使用大括号,如下所示:

 //只有一条语句,则可以不用大括号括住
 i => Console.Write("只有一条语句");

 i => { Console.Write("使用大括号的表达式"); };

 //两条语句时必须要大括号
 i => { i++; Console.Write("两条语句的情况"); };
如果“语句或语句块”有返回值时,如果只有一条语句则可以不写“return”语句,编译器会自动处理,否则必须加上,如下示例:

Code
3、 遵循规则

Lambda表达式是委托的实现方法,所以必须遵循以下规则:
(1)Lambda表达式的参数数量必须和委托的参数数量相同;
2)如果委托的参数中包括有ref或out修饰符,则Lambda表达式的参数列中也必须包括有修饰符;
(3)如果委托有返回类型,则Lambda表达式的语句或语句块中也必须返回相同类型的数据;
4)如果委托有几种数据类型格式而在Lambda表达式中编译器无法推断具体数据类型时,则必须手动明确数据类型。
示例如下:

Code

 小结: C# 2.0规范中提到的匿名方法规范同样适用于Lambda表达式。Lambda表达式是匿名方法在功能行上的超集,提供了下列附加的功能:

(1)Lambda表达式允许省略参数类型并对其进行推断,而匿名方法要求参数类型必须显式地声明。
(2)Lambda表达式体可以是表达式或语句块,而匿名方法体只能是语句块。
(3)在类型参数推导和方法重载抉择时,Lambda表达式可以被作为参数传递。
4)以一个表达式作为表达式体的Lambda表达式可以被转换为表达式树
4、Lambda表达式的转换
两个代表:下面行文中的D代表Delegate(委托类型),L代表Lambda表达式
和匿名方法表达式类似,Lambda表达式可以归类为一种拥有特定转换规则的值。这种值没有类型,但可以被隐式地转换为一个兼容的委托类型。
特别地,当满足下列条件时,委托类型兼容于Lambda表达式:
(1) D和L具有相同数量的参数;
(2) 如果L具有显式类型的参数列表,D中每个参数的类型和修饰符必须和L中相应的参数完全一致;
(3) 如果L具有隐式类型的参数列表,则D中不能有ref或out参数;
(4) 如果D具有void返回值类型,并且L的表达式体是一个表达式,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个可接受为statement-expression的有效表达式;
(5) 如果D具有void返回值类型,并且L的表达式体是一个语句块,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个有效语句块,并且该语句块中不能有带有表达式的return语句;
(6) 如果D的返回值类型不是void,并且L的表达式体是一个表达式,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个可以隐式转换为D的返回值类型的有效表达式;
(7) 如果D的返回值类型不是void,并且L的表达式体是一个语句块,若L的每个参数的类型与D的参数一致,则L的表达式体必须是一个有效的语句块,该语句块不能有可达的终点
(即必须有return语句),并且每个return语句中的表达式都必须能够隐式转换为D的返回值类型。
(ps:ms这里的约束也太多了吧^_^。哎,不管了,先抄下来,以后会熟练运用就行了)

三、一个需要注意的地方
看下面的代码:

Code

运行试一下看看结果,如果你以前碰到过js里的闭包问题,相信你不会大惊小怪(而且可能已经知道了问题的原因),但是,如果你从来没有碰到过这种情况,是不是令你大吃一惊?!输出的竟然不是0,1,2,而是三个3,oh,my god。紧接着,立刻,你会大胆想到这里的list在Add方法执行的地方Add进去的是一个引用类型(这里是lambda表达式()=>i),它们执行的结果共同指向值为3的同一个引用地址!
没错,我们详细分析一下:
1、我们首先定义一个list,其存储格式为func<int>,即返回int型的代理;然后,用for循环将i封装进lambda表达式,并加入到该list中,最后,用foreach循环输出结果。
2、因为lambda表达式实质就是个委托,也就指向一个匿名函数,所以,在foreach输出的时候,使用item()来调用它,让它所指向的函数执行。
至于第2步中item()执行的结果为什么都是3,原因是这样的:
(1)在for循环中,只能有一个 i 变量。即在第一次循环时,i 的地址就分配好了(注意了,这里i的地址第一次分配后是不变的),不会因为循环次数的多少而发生任何改变,其改变的只能是里面装载的值。
(2)lambda表达式在构造时, 传进去的是变量的地址,而不是具体值。只有当真正执行这个lambda表达式时,才会去确定它的值。这就是为什么上面的例子中,其结果均为3(for循环在最后,当i=2时,i又加了1)。

那么如何解决这个问题?解决方案很简单,就是在for循环中,定义一临时变量tmpNum,存储i的值即可。因为编译器会对该临时变量重新分配内存,这样,每次循环,都重新分配新的内存,就不会有这个问题了。
最后把这个“功德圆满”的解决方案贴出来:

Code

好了,Lambda表达式先学到这里,多读多写熟练就不害怕了,继续学习去也。

Code



本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/archive/2009/06/01/1460102.html,如需转载请自行联系原作者
目录
相关文章
C#学习相关系列之数据类型类的三大特性(二)
C#学习相关系列之数据类型类的三大特性(二)
137 1
|
11月前
|
编译器 C# 开发者
C# 9.0 新特性解析
C# 9.0 是微软在2020年11月随.NET 5.0发布的重大更新,带来了一系列新特性和改进,如记录类型、初始化器增强、顶级语句、模式匹配增强、目标类型的新表达式、属性模式和空值处理操作符等,旨在提升开发效率和代码可读性。本文将详细介绍这些新特性,并提供代码示例和常见问题解答。
252 7
C# 9.0 新特性解析
|
编译器 C# Android开发
震惊!Uno Platform 与 C# 最新特性的完美融合,你不可不知的跨平台开发秘籍!
Uno Platform 是一个强大的跨平台应用开发框架,支持 Windows、macOS、iOS、Android 和 WebAssembly,采用 C# 和 XAML 进行编程。C# 作为其核心语言,持续推出新特性,如可空引用类型、异步流、记录类型和顶级语句等,极大地提升了开发效率。要在 Uno Platform 中使用最新 C# 特性,需确保开发环境支持相应版本,并正确配置编译器选项。通过示例展示了如何在 Uno Platform 中应用可空引用类型、异步流、记录类型及顶级语句等功能,帮助开发者更好地构建高效、优质的跨平台应用。
518 59
|
11月前
|
C# 开发者
C# 10.0 新特性解析
C# 10.0 在性能、可读性和开发效率方面进行了多项增强。本文介绍了文件范围的命名空间、记录结构体、只读结构体、局部函数的递归优化、改进的模式匹配和 lambda 表达式等新特性,并通过代码示例帮助理解这些特性。
199 2
|
12月前
|
JSON C# 开发者
C#语言新特性深度剖析:提升你的.NET开发效率
【10月更文挑战第15天】C#语言凭借其强大的功能和易用性深受开发者喜爱。随着.NET平台的演进,C#不断引入新特性,如C# 7.0的模式匹配和C# 8.0的异步流,显著提升了开发效率和代码可维护性。本文将深入探讨这些新特性,助力开发者在.NET开发中更高效地利用它们。
162 1
|
开发框架 自然语言处理 .NET
C#一分钟浅谈:LINQ 查询表达式的使用技巧
【9月更文挑战第6天】LINQ(Language Integrated Query)是C#开发中的强大工具,使查询数据集合变得简单且接近自然语言。本文从基础入手,通过具体示例讲解LINQ查询表达式的使用技巧,包括过滤、排序和分组等操作。同时,文章还探讨了常见问题及解决方法,如性能优化、过早枚举和类型转换等,帮助开发者写出更高效、易维护的代码。
252 16
|
C#
C#一分钟浅谈:Lambda 表达式和匿名方法
本文详细介绍了C#编程中的Lambda表达式与匿名方法,两者均可用于定义无名函数,使代码更简洁易维护。文章通过基础概念讲解和示例对比,展示了各自语法特点,如Lambda表达式的`(parameters) =&gt; expression`形式及匿名方法的`delegate(parameters)`结构。并通过实例演示了两者的应用差异,强调了在使用Lambda时应注意闭包问题及其解决策略,推荐优先使用Lambda表达式以增强代码可读性。
177 8
|
开发框架 .NET 编译器
总结一下 C# 如何自定义特性 Attribute 并进行应用
总结一下 C# 如何自定义特性 Attribute 并进行应用
325 1
|
SQL 开发框架 前端开发
在C#开发中使用第三方组件LambdaParser、DynamicExpresso、Z.Expressions,实现动态解析/求值字符串表达式
在C#开发中使用第三方组件LambdaParser、DynamicExpresso、Z.Expressions,实现动态解析/求值字符串表达式
C#动态查询:巧用Expression组合多条件表达式
在C#中,利用`Expression`类和`AndAlso`、`OrElse`方法,可以组合两个`Expression&lt;Func&lt;T, bool&gt;&gt;`以实现动态多条件查询。该方法通过构建表达式树,方便地构建复杂查询。示例代码展示了如何创建表达式树,分别检查年龄大于等于18和姓名为&quot;John&quot;的条件,并使用`AndAlso`组合这两个条件,最终编译为可执行的委托进行测试。
625 1