C# 基础知识系列- 6 Lambda表达式和Linq简单介绍
前言
c#的lambda和linq可以说是一大亮点,c#的lambda无处不在,linq在数据查询上也有着举足轻重的地位。
那么什么是linq呢,linq是 language intergrated query
(语言集成查询)的缩写,可以对本地对象集合或者远程数据源进行结构化的查询操作。
那什么又是lambda呢?嗯,简单来讲就是匿名函数,我们不声明方法名,只写一个方法体,这个方法体就是lambda表达式
lambda表达式
如何写一个lambda表达式
首先,在写lambda表达式之前,需要先了解 两个特殊的类型:func
和action
。
这是两个委托,这里先不急着了解什么是委托,可以把它们当做一种名称规范就行,它们都可以表示一个方法。不同的是其中func
表示一个有返回值的方法,action
表示一个没有返回值的方法。c#对这两个的定义如下:
public delegate tresult func<out tresult>();//注意这里的out 表示这个泛型是返回值的类型泛型 public delegate void action();
其中func
和action
各有16个变种:
// 注意 in 关键字,表示泛型是参数的类型约束 public delegate tresult func<in t,out tresult>(t arg); public delegate tresult func<in t1,in t2,out tresult>(t1 arg1, t2 arg2); …… public delegate tresult func<in t1,in t2,in t3,in t4,in t5,in t6,in t7,in t8,in t9,in t10,in t11,in t12,in t13,in t14,in t15,in t16,out tresult>(t1 arg1, t2 arg2, t3 arg3, t4 arg4, t5 arg5, t6 arg6, t7 arg7, t8 arg8, t9 arg9, t10 arg10, t11 arg11, t12 arg12, t13 arg13, t14 arg14, t15 arg15, t16 arg16); // // public delegate void action<in t>(t obj); public delegate void action<in t1,in t2>(t1 arg1, t2 arg2); …… public delegate void action<in t1,in t2,in t3,in t4,in t5,in t6,in t7,in t8,in t9,in t10,in t11,in t12,in t13,in t14,in t15,in t16>(t1 arg1, t2 arg2, t3 arg3, t4 arg4, t5 arg5, t6 arg6, t7 arg7, t8 arg8, t9 arg9, t10 arg10, t11 arg11, t12 arg12, t13 arg13, t14 arg14, t15 arg15, t16 arg16);
依次表示一个参数、两个参数、……十六个参数 的方法。当然,你还可以写更多的参数,但是如果一个方法的参数超过10个,为什么不用类封装起来呢?即使不封装,一个方法十几个参数,你确定不会被你的领导嫌弃吗。
言归正传,介绍完了func
和action
的定义,那么如果使用呢?
public void demo1() { // 一个没有返回值,也没有参数的方法 } action act1 = demo;// 直接给一个方法名 public void demo2(string name) { //有一个参数,但没有返回值的方法 } action<string> act2 = demo2; public string demo3() { // 有返回值,但没有参数的方法 } func<string> func1 = demo3; public int demo4(double data) { // 返回值是int,有一个参数是double的方法 } func<double,int> func2 = demo4;
以上是通过方法名获取func
和action
的方法,下面介绍一下通过lambda表达式的方式创建func
和action
:
action act1 = ()=> // lambda 的标志性 声明方式 => { // 这是一个没有返回值,也没有参数的 lambda表达式 }; action<int> act2 = (age) => { // 这是一个 参数为int ,没有返回值的 lambda表达式 }; //========================================= func<string> func1 = () => ""; // 这是一个返回了空字符串的lambda表达式,注意这种写法 func<string> func2 = () => { return ""; //与上一个等价 } func<int,string> func3 = (age) => { return "我的年纪是:"+age;// 一个参数是int,返回类型是string的lambda表达式 }
在lambda表达式中,当使用的是有返回值的方法体时,如果方法体是个简单的计算式或者说可以在一行内写完(或被编译器认为是一行)的话,可以省略 {
、}
和return
,直接用 =>
标记。
比如说以下内容:
func<int,int,int> cal_area = (width, height) => width * height;// 计算面积
使用lambda 表达式
现在我们手里有一大堆的action
和func
,我们该怎么用呢?
有以下两种常见的用法:
-
把它当做方法来用:
// 上接上文代码 act1();// 执行 act1 代表的方法或lambda表达式 act2(10); //执行act2 的lambda表达式 string str1 = func1(); string str2 = func3(10); int area = cal_area(29,39);
-
调用invoke方法:
act1.invoke(); act2.invoke(10); area = cal_area.invoke(33,63);
看过反射篇的应该对invoke有一定印象,这个与methodinfo里的invoke类似,但是比其更加简单。
linq 是什么
正如前言所述,linq是一种对集合、数据源的集成式查询方式,它是对ienumerable<t>
的扩展方法集,所以想要使用linq的话,需要引用两个命名空间 system.linq
和system.linq.expressions
。
linq有两种使用方式,一种是通过方法链的方式调用,一种是类似sql语句的方式进行数据查询。方法链是基础,类sql方式是语法糖。下面简单介绍一下两种方式的使用,不过首先先假设我们有一个数据很多的集合:
ienumerable<int> scores = new list<int>();//假设存放了某班50个人的语文成绩
使用方法链查询
-
获取分数大于60的所有分数:
ienumerable<int> result1 = scores.where(t => t > 60);
-
获取分数大于等于60的数量:
int count = scores.count(t => t >= 60);
-
统计分数总和
int sum = scores.sum();
-
获取所有分数个位上的数字:
ienumerable<int> result2 = scores.select(t => t % 10);
使用类sql形式查询
查询所有大于等于60的分数:
ienumerable<int> result3 = from score in scores where score >= 60 select score;
简单介绍一下,类sql形式有一个统一的格式写法,关键字from
、in
、select
缺一不可:
from 临时变量名 in 数据源
select 结果类型
where 是条件过滤,如果查询全部,可以忽略。
这种方式之所以被我称为是类sql形式,是因为它的写法和sql及其相似,熟悉sql的可以很快上手。
为什么说方法链是基础呢?
因为sql形式的查询里每一个关键字背后都有一个方法作为支撑,除了from 和in。
select 对应的select 方法,where对应的where方法。
需要特别注意的一点:
linq查询是一种延迟查询,也就是说当返回类型是一个ienumerable 的时候不会立即返回结果,必须调用tolist
才能获取到实际查询结果。另外需要注意的是,tolist
返回的是一个不可变list集合,这一点在集合篇中做过介绍了。
未完待续
c#里的linq内容如此丰富,以至于一时间无法详细说明,后续还会有两到三篇关于linq的内容,今天就先到这里了,感谢您的阅读。
更多内容烦请关注