学习LINQ必备条件
转自:http://www.cnblogs.com/VolcanoCloud/p/4451302.html
学习LINQ必备条件:隐式类型局部变量;对象集合初始化器;委托;匿名函数;lambda表达式;扩展方法;匿名类型。废话不多说,我们直接进入主题。
一、实现要求
1、获取全部女生;
2、对满足要求的结果按年龄排序;
3、获取结果的前两名;
4、对获取结果计算平均年龄;
5、输出结果信息,包含姓名、性别、年龄;
说明:学生类为Student(包含学生完整信息),输出结果类为:StudentInfo(包含我们关心的信息,后面将演示它是如何消失的)。在此我们不讨论示例的实用性,使用它,仅是方便引出我们今天的学习内容。
二、代码演进
1、传统方法
现在我们实现第一个要求,找出全部女生。我们平常实现的逻辑大致是:循环学生对象集合,在循环体内逐一判断每一个学生对象的性别是否为要求的性别,如果是则放进结果集合。循环结束后输出结果集合中学生信息对象中的数据(这里我们使用ObjectDumper类来输出信息,它是微软提供的LINQ示例中的一个类)代码出下:
1 /// <summary>
2 /// 获取女生信息并输出
3 /// </summary>
4 public static void GetSutdents() {
5 //由于Student可能会比较多的字段,而我们只输出关心的内容,
6 //因此使用StudentInfo类来存在我们关心的信息
7 List<StudentInfo> studentents = new List<StudentInfo>();
8 foreach (Student student in CreateStudents()) {
9 if (student.Sex == SexType.Woman) {
10 StudentInfo info = new StudentInfo();
11 info.Age = student.Age;
12 info.Sex = student.Sex;
info.Name = student.Name;
13 studentents.Add(info);
14 }
15 }
16 ObjectDumper.Write(studentents);
17 }
2、隐式类型局部变量
上面的方法是我们经常使用的,平常忙于为老板赚钱的我们可能没有时间去考虑上面的代码是否可以精简,项目中到处充斥着类似的逻辑。《重构》告诉我们要尽量消灭重复的代码,以写出优美的、可维护的高质量代码。我们一起来看看上面的代码,它有两个地方出现了重复(studentents、info声名、studentents、info初始化地方)。这里可以精简吗?让我们少敲几下键盘吗?答案当然可以。C#3.0提供了一个新的、名为var的关键词,允许我们无须显示给定类型即可定义一个局部变量。它就是隐式类型局部变量【在使用VAR关键字声明变量,编译器会通过该变量的初始化代码来推断其真正的类型】使用隐式类型局部变量重构上面的代码,如下所示:
1 /// <summary>
2 /// 获取女生信息并输出
3 /// </summary>
4 public static void GetSutdents2() {
5 //由于Student可能会比较多的字段,而我们只输出关心的内容,
6 //因此使用StudentInfo类来存在我们关心的信息
7 var studentents = new List<StudentInfo>();
8 foreach (var student in CreateStudents()) {
9 if (student.Sex == SexType.Woman) {
10 var info = new StudentInfo();
11 info.Age = student.Age;
12 info.Sex = student.Sex;
info.Name = student.Name;
13 studentents.Add(info);
14 }
15 }
16 ObjectDumper.Write(studentents);
17 }
上面的代码声明变量通过var关键字进行,例如: var studentents = new List<StudentInfo>(); 变量studentents的类型是通过后面的初始化表达式new List<StudentInfo>();来推断出变量studentents的类型为List<StudentInfo>。虽然这项改进没有为我们省下多少代码。但如果在整上项目中来看,能为我们节省不少时间,提高效率。需要说明的是:这里不用担心性能问题,因为他和显示声明的写法其实是一样的,只是因为编译器编译器帮我们做了点事,可以通过查看中间语言来证明,因此不要被使用var影响性能的观点所误导,请放心使用。
3、对象初始化器
接下来我们来看一下循环体里对象info的赋值语句,不知道大家有没有对这样的语句感到不舒服,对此我是感觉很不舒服的,但赋值又必须进行,有什么方法能改进吗?增加相应的构造函数看起来是一项不错的选择,至少在能帮我们少写几句代码。但真是这样吗?如果赋值的字段发生变化,怎么办?修改构造函数?这不是有违初衷,本来想少写几行代码,结果不但没有,反而增加了维护的复杂度,因此增加相应的构造函数不是可取的方法。C#3.0引用了对象初始化器【对象初始化器充许我们在单一语句中为对象指定一个或多个字段/属性的值】,这样我们就可以以声明的方式初始化任意类型的对象。在上面的代码中使用对象初始化器改造后如下所示:
1 /// <summary>
2 /// 获取女生信息并输出
3 /// </summary>
4 public static void GetSutdents3() {
5 //由于Student可能会比较多的字段,而我们只输出关心的内容,
6 //因此使用StudentInfo类来存在我们关心的信息
7 var studentents = new List<StudentInfo>();
8 foreach (var student in CreateStudents()) {
9 if (student.Sex == SexType.Woman) {
10 studentents.Add(new StudentInfo() { Age = student.Age, Sex = student.Sex,Name=student.Name });
11 }
12 }
13 ObjectDumper.Write(studentents);
14 }
赋值部份已经由5行变成1行了,这样的改进是不是越来越让我得到了实实在在的好处?
4、委托
至此,上面的代码是不是已经到了无法重构或者非常完善的地步了呢? 如果现在需求发生改变,用户要求查询20岁以下的女生。 这时我们去修改if语句的条件判断?虽然能完成任务,但这种方法不具备可扩展性,因为需求可能又一次发现变化,且更加复杂,此进我们可能需要一单独的方法来做为条件判断,可能是更好的选择。为了更好的通用性,我们不应该把条件写死在方法内部,而应该通过外面传进来。C#2.0提供的委托【委托可以认为是一种对象,用来保存指向函数的指针,类似C++中的函数指针】正好能完成这个任务。现在过滤方法应该是这样的:接受一个Student对象作为参数,返回一个布尔值(代表该对象是否满足特定条件)。我们可以自定义一个指向这类过滤方法(相同的返回类型、相同的参数个数且类型相同(注意:严格来说,这里的表述是不对的,因为C#4.0引入的协变使方法返回的类型可以不相同,逆变使方法的参数类型可以不相同))的委托,但C#2.0提供了能满足我们需求的内置委托类型(delegate Boolean Predicate<T>(T obj);),通过委托来完成新的需要(查询20岁以下的女生)的代码如下:
1 /// <summary>
2 /// 获取女生信息并输出(通过委托实现)
3 /// </summary>
4 public static void GetSutdents4(Predicate<Student> match ) {
5 //由于Student可能会比较多的字段,而我们只输出关心的内容,
6 //因此使用StudentInfo类来存在我们关心的信息
7 var studentents = new List<StudentInfo>();
8 foreach (var student in CreateStudents()) {
9 if (student.Sex == SexType.Woman) {
10 studentents.Add(new StudentInfo() { Age = student.Age, Sex = student.Sex, Name = student.Name });
11 }
12 }
13 ObjectDumper.Write(studentents);
14 }
15
16 /// <summary>
17 /// 条件过滤方法
18 /// </summary>
19 /// <param name="student"></param>
20 /// <returns></returns>
21 private static bool Filter(Student student)
22 {
23 return student.Sex == SexType.Woman && student.Age < 20;
24 }
25
26 /// <summary>
27 /// 主程序
28 /// </summary>
29 /// <param name="args"></param>
30 static void Main(string[] args)
31 {
32 //调用通过委托实现过滤的GetSutdents
33 GetSutdents4(Filter);
34 }
5、匿名函数
虽然我们通过委托解决了通用问题,但增加了一个函数。《重构》中提到的一种重构手法--内联函数(一个函数本体与名称同样清楚易懂时,在函数调用点插入函数本体,然后移除该方法),我们刚提取出来,又内联回去,这不是在做无用功吗?看来我们得在提取和内联之间找到平衡点,C#2.0中的匿名函数【无需声明一个类似Filter的方法,而只需要将这部分逻辑直接传递给GetSutdents4方法即可】正是我们要找的这个平衡点。虽然我们没有声明方法,但编译器会为我们生成,匿名函数增强了委托,降低了代码量。 使用匿名方法调用代码如下:
1 /// <summary>
2 /// 主程序
3 /// </summary>
4 /// <param name="args"></param>
5 static void Main(string[] args) {
6 //使用匿名方法调用GetSutdents4
7 GetSutdents4(delegate(Student student) {
8 return student.Sex == SexType.Woman && student.Age <= 20;
9 }
10 );
11 }
6、Lambda表达式
虽然使用匿名方法已经给我们降低了代码,但可读性确降低了。为此,C#3.0引入了更为简洁的Lambda表达式。它直接将函数编程的精彩表达能力引入到了代码中。它与匿名方法相比提供了如下的一些额外功能(下面4点引用至LINQ IN ACTION):
a、Lambda表达式能够推导出参数的类型,因此程序中无需显式声明;
b、Lambda表达式支持用语句块或表达式作为方法体,语法上比匿名方法更加灵活(匿名方法的方法体只能用语句块);
c、在以参数形式传递时,Lambda表达式能够参与到参数类型推断及对重载方法的选择中。
d、带有表达式体的Lambda表达式能够转化为表达式树;
Lambda表达式的写法如下图所示,它由三个部分组成1、参数;2、Lambda操作符(=>读作:goes to (导出));3、表达式或语句块;
使用Lambda表达式修改后的代码如下:
1 /// <summary>
2 /// 主程序
3 /// </summary>
4 /// <param name="args"></param>
5 static void Main(string[] args) {
6 //使用Lambda表达式调用GetSutdents4
7 GetSutdents4((student=>student.Sex==SexType.Woman && student.Age<20) );
8 }
通过引入Lambda表达式后,代码更加清晰自然了,同时也满足了简明、通用的要求。至此,第一个要求就算完成了。接下来我们将完成排序、获取结果集中前两名女生及计算其平均年龄的功能。
7、扩展方法
如果我们要对上面获取的结果集合排序、计算平均值等操作,使用传统的方法的话,不用说大家也明白要写多少代码吧,这里我们就不再去讲述传统方法了,直接进入主题。我们将使用扩展方法来实现上面的要求,同时也展现使用LINQ带的扩展方法的魔力。扩展方法【用来在类型定义完成后,由于某些原因不能修改源类型的情况下,继续为基添加新方法】C#中定义扩展方法必须在非泛型的静态类中定义一个静态方法,此方法能够接受任意多个参数,但是第一个参数的类型必须和所扩展的类型一致,且用this关键修饰。LINQ为我们带来了一系列的扩展方法(不管是否用到了LINQ,我们都可以根据实际需要使用他们)。使用扩展方法修改后代码如下所示:
1 /// <summary>
2 /// 获取女生信息并输出
3 /// </summary>
4 public static void GetSutdents5(Predicate<Student> match) {
5 //由于Student可能会比较多的字段,而我们只输出关心的内容,
6 //因此使用StudentInfo类来存在我们关心的信息
7 var studentents = new List<StudentInfo>();
8 foreach (var student in CreateStudents()) {
9 if (student.Sex == SexType.Woman) {
10 studentents.Add(new StudentInfo() { Age = student.Age, Sex = student.Sex, Name = student.Name });
11 }
12 }
13
14 //按年龄排序后获取前两个女学生并求年龄的平均值
15 var average = studentents.OrderBy(s => s.Age)
16 .Take(2)
17 .Average(s => s.Age);
18 ObjectDumper.Write(average);
19 }
看到这些扩展方法带来的魔力了吧,后面几个要求,被这么简单的一句链式调用【通过“.”对所需方法进行连续调用,就像串起来的链一样】就完成了。其中OrderBy为定义于System.Linq.Enumerable类中的扩展方法。它们的用途,这里就不赘述了。
8、匿名类型
这是开始LINQ之前的最后一个重量级的的C#语言特性了,匿名类型【能像对象初化器一样构建事先没有定义的类型,编译器会帮我定义(又是编译器,真是一位强大的助手,也正是因为编译器在后面帮我们做了幕后工作,虽然这些在C#3.0中定义的语言特性编译后能运行在.NET2.0上,而无需引用那庞大的.NET3.0或.NET3.5,当然上面用到的扩展方法需要System.Runtime.CompilerServices.ExtensionAttribute属性的支持。但我们可以自行引用入或将System.Core.dll和.NET2.0一起分发)】 上面我们为了返回关心的学生信息而定义一个StudentInfo类,其实有了匿名类型后,像这种简单的类,可以不用特意去定义,从而节约时间。这能减少系统中的一些无行为的(只是一个数据容器)的杂乱的类。使用匿名类型修改后的代码如下:
1 /// <summary>
2 /// 获取女生信息并输出(使用匿名类型获取关心的信息)
3 /// </summary>
4 public static void GetSutdents6() {
5 var studentents = new List<object>();
6 foreach (var student in CreateStudents()) {
7 if (student.Sex == SexType.Woman) {
8 studentents.Add(new { Age = student.Age, Sex = student.Sex, Name = student.Name });
9 }
10 }
11 ObjectDumper.Write(studentents);
12 }
从上面的代码,我们可以看到,StudentInfo类型不见了,它已经被匿名类型 new { Age = student.Age, Sex = student.Sex, Name = student.Name } 所代替。
温习那曾经熟悉却又不小心忘记的知识。如有什么不恰当的地方,恳请指正!
学习LINQ必备条件的更多相关文章
- 白话LINQ系列2---以代码演进方式学习LINQ必备条件
今天,我们通过一个简单的示例代码的演进过程,来学习LINQ必备条件:隐式类型局部变量:对象集合初始化器:委托:匿名函数:lambda表达式:扩展方法:匿名类型.废话不多说,我们直接进入主题. 一.实现 ...
- 获得CCNA和CCNP及CCIE认证的必备条件和有效期绍
CCNA认证培训介绍 CCNA认证(CCNA-思科网络安装和支持认证助理)是整个Cisco认证体系中最初级的认证,同时它也是获得CCNP认证.CCDP认证和CCSP认证的必要条件(CCIP认证.CCI ...
- LinqPad工具:帮你快速学习Linq
LinqPad工具:帮你快速学习Linq 参考: http://www.cnblogs.com/li-peng/p/3441729.html ★:linqPad下载地址:http://www.linq ...
- 学习LINQ,发现一个好的工具。LINQPad!!
今日学习LINQ,发现一个好的工具.LINQPad!! 此工具的好处在于,不需要在程序内执行,直接只用工具测试.然后代码通过即可,速度快,简洁方便. 可以生成其LINQ查询对应的lambda和SQL语 ...
- SHELL学习笔记----IF条件判断,判断条件
SHELL学习笔记----IF条件判断,判断条件 前言: 无论什么编程语言都离不开条件判断.SHELL也不例外. if list then do something here ...
- 【转】福利大放送--不止是Android,Github超高影响力开源大放送,学习开发必备教科书
[福利大放送]不止是Android,Github超高影响力开源大放送,学习开发必备教科书 目录 一.写在前面 1.free-programming-books 2.oh-my-zsh 3.awes ...
- mac废纸篓清空的心得、mac设置不睡眠不待机不锁屏、如何快速锁屏待机睡眠、mac重启、mac学习的必备软件-城
mac废纸篓清空: 1.使用废纸篓的清空废纸篓,清空所有包括被锁定的文件: 2.使用“磁盘工具”的“修复磁盘权限”,修复完成再操作清空废纸篓: 3.使用cleanmymac软件“垃圾清理”和“擦除器” ...
- 转 --简单解决Linq多条件组合问题
本文笔者用清晰的实例,解决了Linq多条件问题,思路十分的清晰,笔者也很细心的做了描述,希望能给你带来帮助. 最近有个项目准备功能改版,师兄吩咐:尽可能地做到万般皆Linq,所以很多东西都要从存储过程 ...
- js_html_input中autocomplete="off"在chrom中失效的解决办法 使用JS模拟锚点跳转 js如何获取url参数 C#模拟httpwebrequest请求_向服务器模拟cookie发送 实习期学到的技术(一) LinqPad的变量比较功能 ASP.NET EF 使用LinqPad 快速学习Linq
js_html_input中autocomplete="off"在chrom中失效的解决办法 分享网上的2种办法: 1-可以在不需要默认填写的input框中设置 autocompl ...
随机推荐
- 关于DebuggerHidden特性在Unity中的使用
经过测试,DebuggerHidden只对输出的日志有影响.对编辑器的控制台输入没有影响 你可以通过这个特性避免日志log有一堆的堆栈信息 并且测试过发现Debug.LogError也不支持该特性,和 ...
- p4n 今天与朋友沟通支付云服务普及以及跨境电子商务的光辉前景
p4n 今天与朋友沟通支付云服务普及以及跨境电子商务的光辉前景 跨境电子商务也是个光忙四色和的跨境电子商务啊..支付项目也是个强大的项目.. 过几天我们就要宣布正式发布atipay ,并宣称将致力于推 ...
- atitit.自己动手开发编译器and解释器(1) ------词法分析--attilax总结
atitit.自己动手开发编译器and解释器(1) ------词法分析--attilax总结 1. 应用场景:::DSL 大大提升开发效率 1 2. 2. 流程如下::: 词法分析(生成toke ...
- FreeRtos——任务删除,改变任务优先级
以下转载自安富莱电子: http://forum.armfly.com/forum.php vTaskDelete() API 函数任务可以使用 API 函数 vTaskDelete()删除自己或其它 ...
- CSS学习笔记(8)--纯CSS绘制三角形(各种角度)
纯CSS绘制三角形(各种角度) CSS三角形绘制方法,学会了这个,其它的也就简单. 我们的网页因为 CSS 而呈现千变万化的风格.这一看似简单的样式语言在使用中非常灵活,只要你发挥创意就能实现很多 ...
- 悦铃文件必须是CCITT A_Law格式的,且没有被压缩
最近在给公司弄来电彩铃,用的是电信的“悦铃”业务,办理过程不想多说了..给了我个网址和账号让我登录,登录界面惨不忍睹,感觉电信根本没有要宣传这项业务的意思,像是粗制滥造外包赶工做出来的.. 当然这不是 ...
- 一款基于jQuery的图片下滑切换焦点图插件
之前为大家分享了好多款jquery插件,今天我们要分享的一款jQuery插件也比较实用,是一款jQuery焦点图插件.焦点图相当普通,一共可以循环播放4张图片,并且每一张图片在切换的时候都是向下滑动的 ...
- Eclipse4.4 安装java反编译插件Eclipse Class Decompiler
一.在线安装方式: Eclipse Class Decompiler整合了眼下最好的2个Java反编译工具Jad和JD-Core,而且和Eclipse Class Viewer无缝集成.可以非常方便的 ...
- wpa wp2 psk的配置方式
For WPA-PSK ctrl_interface=/var/run/wpa_supplicant ctrl_interface_group=0 eapol_version=1 fast_reaut ...
- RabbitMQ之任务队列【译】
在第一个教程里面,我们写了一个程序从一个有名字的队列中发送和接收消息,在这里我们将要创建一个分发耗时任务给多个worker的任务队列. 任务队列核心思想就是避免执行一个资源密集型的任务,而程序要等待其 ...