[.net 面向对象程序设计进阶] (5) Lamda表达式(一)  创建委托

本节导读:

通过学习Lambda表达式,学会创建委托和表达式目录树,深入了解Lambda的特性,让你的代码变的更加清晰、简洁、高效。

读前必备:

本节学习前,需要掌握以下知识:

A.泛型        (请参考[.net 面向对象编程基础]  (18) 泛型

B.Linq基础 (请参照[.net 面向对象编程基础] (19) LINQ基础

C.Linq使用  (请参照[.net 面向对象编程基础]  (20) LINQ使用)

D.委托       (请参照[.net 面向对象编程基础]   (21) 委托

E.事件      (请参照[.net 面向对象编程基础]   (22) 事件

通过《.net 面向对象编程基础》系列中相关介绍,我们已经初步使用过了Lambda表达式进行Linq查询,这节我们主要深入了解Lambda表达式。

1. 关于Lambda

Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数。

以上是微软对Lambda表达式的定义。从这个定义中,我们可以看出,Lambda的存在,主要做两件事:

A.创建委托(Delegate)

B.创建表达式树(Expression Tree)

此外,Lambda表达式的本质就是匿名方法(或叫匿名函数)。

后面面我们分别从这两个方法入手,进一步学习Lambda带我给我们的便利。

2. Lambda表达式

Lambda表达式的构成如下:

零个参数: ()=>expr

一个参数:(param)=>expr 或 param=>expr

多个参数:(param-list)=>expr

所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to"

当参数只有一个时,右边的括号可以省略。

下面是写法举例:

//零个参数
() => MethodName()
//一个参数
(x) => x+x
X=>x+x
//两个及以上参数
(m,n) => m.Length>n
//显式类型参数
(int x,string y)=>y.Length>x

上面的示例中,其中显式类型参数,是当编译器无法推断其参数类型时,可以显式的定义参数类型。

 3. Lambda语句

Lambda语句和Lambda表达式类似,只是右边部分写在{}中

Lambda语句构成如下:

(input parameters) => {statement;}

示例:

delegate void TestDelegate(string s);

TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };
myDel("Hello");

4.异步Lambda(async和await)

我们通过在《.net 面向对象编程基础》中学习,了解了事件本身也是一种特殊的委托,那么Lambda可用于创建委托也就是说同样可以来创建事件。

下面看示例:

首先看一个普通按钮点击事件的实现及Lambda语句写法

下面是普通写法:

 this.myButton.Click += new System.EventHandler(this.myButton_Click);

//事件通常委托写法
private void myButton_Click(object sender, EventArgs e)
{
MessageBox.Show("您好,我是按钮点击事件!");
}

下面是Lambda写法:

//事件Lambda语句写法
myButton.Click += (sender, e) => { MessageBox.Show("您好,我是按钮点击事件!这是Lambda语句写法"); };

上面两种写法,我们在前面的章节中已经有很多例子了。

下面我们看一下异步的事件:

//异步事件普通写法
private async void asyncButton_Click(object sender, EventArgs e)
{
await MyMethodAsync();
MessageBox.Show("您好,我是按钮点击事件!异步的哟!"); }
async Task MyMethodAsync()
{
await Task.Delay();
}

我们用Lambda来改写上面的异步事件:

//异步事件Lambda写法
asyncButton.Click += async (sender, e) => { await MyMethodAsync(); MessageBox.Show("您好,我是按钮点击事件!异步的哟!这是Lambda语句写法"); };
async Task MyMethodAsync()
{
await Task.Delay();
}

上面的异步事件,就是在事件委托阶段使用async来表示这是一个异步的,在事件处理中阶段使用await关键词来指定一个异步方法。

Lambda语句写法同样是针对异步事件的简洁写法,具有相同的效力。

5. 标准查询运算的Lambda表达式

5.1泛型委托 使用Lambda

我们在[.net 面向对象编程基础]   (21) 委托一节中说到了三种常用的泛型委托

Action(无返回值泛型委托)

Func(有返回值泛型委托)

predicate(返回值为bool型的泛型委托)

这节不再重复说明泛型委托,不熟悉泛型委托的小伙伴,请参考,我们只举例说明泛型委托和它的Lambda写法

//Action 无返回值类型的 泛型委托
//匿名方法声明及调用
Action<int, int> act = delegate (int a, int b) {
Console.WriteLine(a + "+" + b + "=" + (a + b));
};
act(, );
//表达式声明及调用
Action<int, int> actLambda = (a, b) => { Console.WriteLine(a + "+" + b + "=" + (a + b)); };
actLambda(, ); //Func 带返回值的 泛型委托
//匿名方法声明及调用
Func<int, int, string> acc = delegate (int a, int b) {
return (a + "+" + b + "=" + (a + b));
};
Console.WriteLine(acc(, ));
//表达式声明及调用
Func<int, int, string> ac = (a, b) => { return (a + "+" + b + "=" + (a + b)); };
Console.WriteLine(ac(, ));

5.2 Linq中使用Lambda

关于在Linq中使用Lambda表达式,我们在[.net 面向对象编程基础]  (20) LINQ使用有详细说明了,不熟悉的小伙伴请参考,下面我们举例说明几种常用的Lambda查询写法。

5.2.1 Count 求数量

//查询下列数中的奇数 - Lambda写法
int[] numbers = { , , , , , , , , , };
int oddNumbers = numbers.Count(n => n % == );
Console.WriteLine("奇数有:"+oddNumbers.ToString()+ "个,Lambda写法");
//查询下列数中的奇数 -Linq查询写法
var oddNumbersFunc = (from num in numbers
where num % ==
select num
).Count(); Console.WriteLine("奇数有:" + oddNumbersFunc.ToString() + "个,Linq查询写法");

运行结果如下:

5.2.2  TakeWhile 满足条件就返回集合

//TakeWhile只要满足指定的条件就返回,在检检到number中的6时,就返回,其中8 和9 不满足条件,则返回 5 4 1 3
int[] numbers = { , , , , , , , , , };
var firstNumbersLessThan6 = numbers.TakeWhile(n => n < );
Console.WriteLine("满足条件就返回的数有:" + firstNumbersLessThan6.Count().ToString() + "个,Lambda写法");
//第二个参数index表示n的索引位置
string NewNumber = String.Empty;
numbers.TakeWhile((n, index) => n >= index).ToList().ForEach(m=> NewNumber+=m +" ");
Console.WriteLine("满足条件就返回的数有:" + NewNumber + ",Lambda写法");

运行结果如下:

6. Lambda的类型推理

在编写 lambda 时,通常不必为输入参数指定类型,因为编译器可以根据 lambda 主体、参数的委托类型以及 C# 语言规范中描述的其他因素来推断类型。 对于大多数标准查询运算符,第一个输入是源序列中的元素类型。 因此,如果要查询 IEnumerable<Customer>,则输入变量将被推断为 Customer 对象,这意味着你可以访问其方法和属性:

//Lambda的类型推理
List<MyClass> list = new List<MyClass>() {
new MyClass(){ at1 = "aaa", at2 = , at3 = DateTime.Now},
new MyClass{ at1 = "bbb", at2 = , at3 = DateTime.Parse("2015-06-07") },
new MyClass{ at1 = "aaa", at2 = , at3 = DateTime.Parse("2010-11-12") }
};
Console.WriteLine("at1为aaa的元素有:" + list.Where(m => m.at1 == "aaa").Count() + "个,Lambda写法"); 
class MyClass {
public string at1 { get; set; }
public int at2 { get; set; }
public DateTime at3 { get; set; }
}

运行结果如下:

Lambda 的一般规则如下:

A. Lambda 包含的参数数量必须与委托类型包含的参数数量相同。

B. Lambda 中的每个输入参数必须都能够隐式转换为其对应的委托参数。

C. Lambda 的返回值(如果有)必须能够隐式转换为委托的返回类型。

请注意,lambda 表达式本身没有类型,因为常规类型系统没有“Lambda 表达式”这一内部概念。但是,有时以一种非正式的方式谈论 lambda 表达式的“类型”会很方便。 在这些情况下,类型是指委托类型或 lambda 表达式所转换到的 Expression 类型。

7. Lambda表达式的变量范围

我们在[.net 面向对象编程基础]  (19) LINQ基础 一节中说到匿名方法时提到匿名方法可以引用满园内的外部变量,前面示例中也有提及。

下面我们看一个示例:

class Program
{
static int num = ;
static void Main(string[] args)
{
int num2 = ; //Lambda表达式的变量范围
//相对以下表达式内部,一个类的字段num和一个变量num2,下面测试在表达式内部调用
int[] numbers2 = { , , , , , , , , , };
//我们对大于num小于num2的数求和
int sum = numbers2.Where(m => m > num && m < num2).Sum();
Console.WriteLine("大于num小于num2的和为:" + sum);
Console.ReadKey();
}
}

下列规则适用于 lambda 表达式中的变量范围:

A.捕获的变量将不会被作为垃圾回收,直至引用变量的委托符合垃圾回收的条件。

B.在外部方法中看不到 lambda 表达式内引入的变量。

C.Lambda 表达式无法从封闭方法中直接捕获 ref 或 out 参数。

D.Lambda 表达式中的返回语句不会导致封闭方法返回。

E.如果跳转语句的目标在块外部,则 lambda 表达式不能包含位于 lambda 函数内部的 goto 语句、break 语句或 continue 语句。 同样,如果目标在块内部,则在 lambda 函数块外部使用跳转语句也是错误的。

8.要点:

本节主要说明了:

Lambda表达式在创建委托中的应用;

Lambda表达式在Linq查询中的应用;

Lambda 语句在异步事件中的应用;

Lambda 的两个特性:类型推理和外部变量引用

下一节,我们主要说明Lambda的另一个特点,就是创建表达式目录树(Expression Tree)。

==============================================================================================

 返回目录

 <如果对你有帮助,记得点一下推荐哦,如有

有不明白或错误之处,请多交流>

<对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>

<转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>

.NET 技术交流群:467189533   

==============================================================================================

[.net 面向对象程序设计进阶] (5) Lamda表达式(一) 创建委托的更多相关文章

  1. [.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门

    [.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习 ...

  2. [.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用

    [.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用 本节导读:讨论了表达式树的定义和解析之后,我们知道了表达式树就是并非可执行代码,而是将表达式对象化后的数据结构.是 ...

  3. [.net 面向对象程序设计进阶] (1) 开篇

    [.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对 ...

  4. [.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上)

    [.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上) 本节导读: 随着硬件和网络的高速发展,为多线程(Multithreading) ...

  5. [.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手

    [.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方 ...

  6. [.net 面向对象程序设计进阶] (2) 正则表达式 (一) 快速入门

    [.net 面向对象程序设计进阶] (2) 正则表达式 (一) 快速入门 1. 什么是正则表达式? 1.1 正则表达式概念 正则表达式,又称正则表示法,英文名:Regular Expression(简 ...

  7. [.net 面向对象程序设计进阶] (3) 正则表达式 (二) 高级应用

    [.net 面向对象程序设计进阶] (2) 正则表达式 (二)  高级应用 上一节我们说到了C#使用正则表达式的几种方法(Replace,Match,Matches,IsMatch,Split等),还 ...

  8. [.net 面向对象程序设计进阶] (28) 结束语——告别2015

    [.net 面向对象程序设计进阶] (28) 结束语——告别2015 <.net面向对象程序设计进阶>这一系列文章写了太长的时间了,大概有半年没写,在年底又一口气写了好几篇.在整个过程中目 ...

  9. [.net 面向对象程序设计进阶] (27) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git

    [.net 面向对象程序设计进阶] (26) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git 本篇导读: 接上两篇,继续Git之旅 分布式版本控制系统 ...

随机推荐

  1. zookeeper学习(一)安装、配置、运行

    说明:zookeeper完全可以standalone,也可以伪集群形式,当然生产中都是集群形式.另外,也可以在windows下运行. 如果只是研究用,完全可以在windows下使用standalone ...

  2. c# dotfuscator 混淆后无法使用

    在实体类中忘记给字段加上 get ;set ;导致编译后程序无法使用. 下面这个(A代码)是可以正常混淆的.     public class PhoneUsedStatus     {        ...

  3. 面向对象的OOA、OOD、OOP

    OOA Object-Oriented Analysis:面向对象分析方法 是在一个系统的开发过程中进行了系统业务调查以后,按照面向对象的思想来分析问题.OOA与结构化分析有较大的区别.OOA所强调的 ...

  4. python 装饰器修改调整函数参数

    简单记录一下利用python装饰器来调整函数的方法.现在有个需求:参数line范围为1-16,要求把9-16的范围转化为1-8,即9对应1,10对应2,...,16对应8. 下面是例子: def fo ...

  5. PHP对象转数组||PHP数组转对象

    function arrayToObject($e){     if( gettype($e)!='array' ) return;     foreach($e as $k=>$v){     ...

  6. Good-Bye

    嘛……以一种奇怪的姿势滚粗了…… 如果这个Blog能给未来的OIer们一些帮助的话,它也不枉存在了…… 我的OI之路也能以另一种形式延续下去吧…… 也许能搞ACM的话会再开?…… 不管怎么说,各位再见 ...

  7. 一个实现了View接口的Fragment

    小程序并不新鲜,模式上先有百度轻应用,后有支付宝的各类小服务,再来还有腾讯自家QQ右下角的应用宝:技术上也就是FaceBook RN的那一套.一个技术上无创新,形式上无创意的事物,凭什么勾起了开发者们 ...

  8. Partial backup 备份指定表/库

    Partial Backups XtraBackup支持partial backups,这意味着你可以只备份部分表或库.要备份的表必须是独立表空间,即innodb_file_per_table=1 有 ...

  9. PHP-Mysqli扩展库的预编译

    (1)预编译的好处 假如要执行100条类似的sql语句,每一次执行,在MySQL端都会进行一次编译,效率很低.提高效率的方法就是--减少编译的次数. 先制造一个sql语句的模板,在MySQL端预先编译 ...

  10. ASP.NET Web服务调用发生错误,错误代码404

    现象: iOS端使用ASIHTTP连接Web服务时,得到的数据是一个错误代码为404的页面,错误信息(web.config添加<customErrors mode="Off" ...