24.C# Lambda表达式
1.Lambda表达式的含义
Lambda表达式是C#3.0引入的一种结构,使用它可以简化C#编程。
2.Lambda表达式与匿名方法
我们知道匿名方法可用于事件处理,如下delegate声明了一个匿名方法,它用于timer的Elapsed事件。
- System.Timers.Timer timer = new System.Timers.Timer();
- timer.Elapsed+= delegate(object source,ElapsedEventArgs e){
- Console.WriteLine("Event handler called after {0} miliseconds.", (source as System.Timers.Timer).Interval);
- };
在我看来匿名函数的实质上就是用一个没有函数名称的方法创建一个委托,它是一个委托,因为它纯粹是为了委托而存在的。
如果不使用匿名方法,就需要创建匹配该事件的委托实例来订阅事件,比如
- System.Timers.Timer timer = new System.Timers.Timer();
- timer.Elapsed += new ElapsedEventHandler(TimerElapsed);
- public static void TimerElapsed(object source,ElapsedEventArgs e)
- {
- Console.WriteLine("Event handler called after {0} miliseconds.", (source as System.Timers.Timer).Interval);
- }
也可以使用如下写法,编译器会自动为把TimerElapsed方法封装成对应的委托,但一般不这样写,这样会让人不好理解。
- timer.Elapsed += TimerElapsed;
我们可以使用lambda表达式替代前面的匿名方法,编译器会用lambda表达式创建一个匿名方法,该匿名方法和之前的相同或者相似。可以说lambda表达式本质上是一个匿名方法,也就算是一个委托。
- System.Timers.Timer timer = new System.Timers.Timer();
- timer.Elapsed += (source, e) => Console.WriteLine("Event handler called after {0} miliseconds.", (source as System.Timers.Timer).Interval);
- timer.Start();
3.lambda表达式的构成
lambda表达式由3部分构成:
1)参数列表
2)=>运算符
3)表达式体
参数列表类似于方法参数,可以不定义参数类型,lambda表达式会使用类型推理功能根据实质传递的参数确定参数的类型,也可以显示定义参数类型,但是如果显示定义了参数类型,所有的参数就必须定义参数类型。
- (int a, int b)=> a+b;
显式定义参数比较好理解,但是不够灵活,如下的定义可以使用long等其他类型参数,上面的表达式只能使用int。
- (a,b)=>a+b;
参数列表有多个参数时用逗号分开,只有一个参数时可以不用括号,如
- a=>a*a
也可以定义没有参数的lambda表达式
- ()=>Math.PI
=>运算符把参数列表和表达式分开
表达式体类似于方法体,lambda表达式表达式体可以包含多个语句,有返回值且包含多个语句的lambda表达式如果需要用return关键字指定返回值,单个语句可以不用return,比如
- (a,b)=>{
- //其他语句...
- return a+b;
- }
大多数情况下只有在语句较少的情况使用lambda表达式,语句比较多还是创建一个方法,方便重用。
4.Lambda表达式和系统定义的委托Action、Action<>、Func<>
如前所述,我们指定lambda表达式实际上是一个委托,那么我们就可以用lambda表达式表示系统定义的如下委托,它们在System命名空间中定义
1)Action表示的lambda表达式不带参数,没有返回值;
Action的定义
- namespace System
- {
- // 摘要:
- // 封装一个方法,该方法不具有参数并且不返回值。
- [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]
- public delegate void Action();
- }
2)Action<>表示的lambda表达式至多带16个参数(至少带一个参数),没有返回值(基于4.0)
其中一个委托的定义
- namespace System
- {
- // 摘要:
- // 封装一个方法,该方法只有一个参数并且不返回值。
- //
- // 参数:
- // obj:
- // 此委托封装的方法的参数。
- //
- // 类型参数:
- // T:
- // 此委托封装的方法的参数类型。
- public delegate void Action<in T>(T obj);
- }
3)Func<>表示的lambda表达式至多带16个参数(可以不带参数),有返回值
其中一个委托的定义
- namespace System
- {
- // 摘要:
- // 封装一个具有一个参数并返回 TResult 参数指定的类型值的方法。
- //
- // 参数:
- // arg:
- // 此委托封装的方法的参数。
- //
- // 类型参数:
- // T:
- // 此委托封装的方法的参数类型。
- //
- // TResult:
- // 此委托封装的方法的返回值类型。
- //
- // 返回结果:
- // 此委托封装的方法的返回值。
- [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]
- public delegate TResult Func<in T, out TResult>(T arg);
- }
不带参数的委托
- namespace System
- {
- // 摘要:
- // 封装一个不具有参数但却返回 TResult 参数指定的类型值的方法。
- //
- // 类型参数:
- // TResult:
- // 此委托封装的方法的返回值类型。
- //
- // 返回结果:
- // 此委托封装的方法的返回值。
- [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]
- public delegate TResult Func<out TResult>();
- }
Func<>泛型中,返回值总是在最后一个参数,比如
- Func<int,int,int> a = (b,c)=>b+c;
5.Lambda表达式和集合
学习了Func<>就可以理解System.Linq命名空间为数组系列提供的一些方法了,比如累加器Aggregate()方法。Aggregate()方法有三个重载版本,如下
- public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func);
- public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);
- public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector);
这几个重载版本都是对数组的每个元素应用累加器函数,每个版本略有不同:
第一个版本比较简单,它把数组的第一第二个元素用作累计器函数的参数,计算得出的结果用作参数参与第二轮计算,和数组第三个元素一起再次传入累加器函数,这样依次叠加,直到最后一个元素。
第二个版本返回值类型可以和数组类型不一样,它输入一个初始值,依次和每一个数组元素累加起来。
第三个版本在第二个版本的基础上,允许我们在使用累计器函数得到的结果上用指定的函数选择结果
如下是一个例子
- class Program
- {
- static void Main(string[] args)
- {
- string[] curries = { "pathia", "jalfreze", "korma" };
- //累加字符串数组
- Console.WriteLine(curries.Aggregate((a, b) => a + " " + b));
- //计算字符串数组总长度
- Console.WriteLine(curries.Aggregate(, (a, b) => a + b.Length));
- //a=>a表示参数和返回类型同样的匿名函数,在此表示返回用累加器叠加后得到的字符串本身
- Console.WriteLine(curries.Aggregate("Some curries:", (a, b) => a + " " + b, a => a));
- //a=>a表示参数和返回类型同样的匿名函数,在此表示返回用累加器叠加后得到的的字符串本身的长度
- Console.WriteLine(curries.Aggregate("Some curries:", (a, b) => a + " " + b, a => a.Length));
- Console.ReadLine();
- }
- }
注上面的例子中没有明确指定泛型类型,比如
- Console.WriteLine(curries.Aggregate<string>((a, b) => a + " " + b));
- Console.WriteLine(curries.Aggregate<string,int>(, (a, b) => a + b.Length));
因为实参的类型可以使用类型推理得来,所有以上等价于
- Console.WriteLine(curries.Aggregate((a, b) => a + " " + b));
- //计算字符串数组总长度
- Console.WriteLine(curries.Aggregate(, (a, b) => a + b.Length));
当不能使用类型推理得到参数的类型,编译器会要求显式标明参数类型。
24.C# Lambda表达式的更多相关文章
- JDK8 的 Lambda 表达式原理
JDK8 使用一行 Lambda 表达式可以代替先前用匿名类五六行代码所做的事情,那么它是怎么实现的呢?从所周知,匿名类会在编译的时候生成与宿主类带上 $1, $2 的类文件,如写在 TestLamb ...
- C#中的Lambda表达式和表达式树
在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在 ...
- Java 8特性探究(1):通往lambda之路与 lambda表达式10个示例
本文由 ImportNew 函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的ja ...
- python中lambda表达式应用
对于简单的函数,也存在一种简便的表示方式,即:lambda表达式 #普通函数1 def func(a): return a+1 print 'test1_func0:',func(1000)4#lam ...
- 【转载】C++ function、bind和lambda表达式
本篇随笔为转载,原贴地址:C++ function.bind和lambda表达式. 本文是C++0x系列的第四篇,主要是内容是C++0x中新增的lambda表达式, function对象和bind机制 ...
- C#在泛型类中,通过表达式树构造lambda表达式
场景 最近对爬虫的数据库架构做调整,需要将数据迁移到MongoDB上去,需要重新实现一个针对MongoDB的Dao泛型类,好吧,动手开工,当实现删除操作的时候问题来了. 我们的删除操作定义如下:voi ...
- lambda表达式、内置函数、进制和文件操作
lambda表达式 定义函数(普通方式)def f1(): return 123 f2 = lambda : 123 def f3(a1,a2): return a1+a2 定义函数(lambda表达 ...
- (转)C#中的委托,匿名方法和Lambda表达式
简介 在.NET中,委托,匿名方法和Lambda表达式很容易发生混淆.我想下面的代码能证实这点.下面哪一个First会被编译?哪一个会返回我们需要的结果?即Customer.ID=5.答案是6个Fir ...
- java 8 中lambda表达式学习
转自 http://blog.csdn.net/renfufei/article/details/24600507 http://www.jdon.com/idea/java/10-example-o ...
随机推荐
- Java桥接模式
定义:将抽象部分与它的具体实现部分分离,使他们都可以独立的变化 通过组合的方式建立两个类之间联系,而不是继承 类型:结构型 适用场景: 抽象和具体实现之间增加更多的灵活性 一个类存在两个(或多个)独立 ...
- php实现微信企业向用户付款
<?php header('content-type:text/html;charset=utf-8'); $data['mch_appid']='##################';//商 ...
- JavaScript 动态选择方法和属性
<html> <head> <meta http-equiv="Content-Type" content="text/html; char ...
- 全文搜索引擎——Solr
1.部署solr a.下载并解压Solr b.导入项目(独立项目): 将解压后的 server\solr-webapp 下的 webapp文件夹 拷贝到tomcat的webapps下,并重命名为 so ...
- B - Housewife Wind-树链剖分-树状数组
思路:边权转化到点权上,统一把每一条边的边权集中到深度较深的点上去. #include<stdio.h> #include<iostream> #include<cstr ...
- 重启部署在阿里云上的huginn
背景,因为重新编译安装了gcc,不知怎么服务器上的huginn就停了, 因为之前是安装在docker上的,服务器重启之后需要:1.启动dockerservice docker start2.命令创建h ...
- PostgreSQL自学笔记:8 查询数据
8 查询数据 8.1 基本查询语句 select语句的基本格式是: select {* | 字段1[,字段2,...]} [ from 表1,表2... [where 表达式] [group by & ...
- 微信tinker 热修复
Tinker 是微信官方的Android热补丁解决方案,它支持动态下发代码.So库以及资源,让应用能够在不需要重新安装的情况下实现更新.当然,你也可以使用Tinker来更新你的插件. github:h ...
- helm-chart7,调试与hook
调试 几个命令可以帮助进行调试 helm lint 首选工具,返回错误和警告信息. helm install --dry-run --debug:服务器会渲染你的模板,然后返回结果清单文件. helm ...
- CSS3_盒子背景
盒子背景 盒子背景:content padding 特殊的 boder 背景 背景绘制 从 padding 开始绘制 背景裁剪 background-clip(默认值 border-box ...