Linq 查询的演变过程
Linq查询
1.linq简介
说起Linq,相信大家都不陌生吧,因为在我们的C#开发中,Linq无处不在,Linq给我们带来了极大的便捷。.Net 3.0后,Linq横空出世,为我们带来极大的方便。
Linq查询主要包含两个方面的内容:Linq to Object和Linq to sql
笔者常用到的就是Linq to Object,Linq to sql接触的较少,还需接下来花时间学习。
我们在使用Linq过程中,有没有考虑过Linq是怎么产生的呢?
2.Linq to object演变
假如我们现在有一个下面代码所示的LIST<Student>集合
static void Initlist(out List<student> ListStu) { ListStu = new List<student>(); student s = new student { Name = "张三", Sex = "男", Age = 20 }; ListStu.Add(s); student s1 = new student { Name = "李四", Sex = "男", Age = 20 }; student s2 = new student { Name = "王五", Sex = "男", Age = 21 }; student s3 = new student { Name = "刘一", Sex = "女", Age = 22 }; student s4 = new student { Name = "赵重", Sex = "男", Age = 23 }; student s5 = new student { Name = "李玉", Sex = "女", Age = 24 }; ListStu.Add(s1); ListStu.Add(s2); ListStu.Add(s3); ListStu.Add(s4); ListStu.Add(s5); }
现在我们有下面三个要求:
1.选出年龄大于21岁的
2.选出年龄大于20的
3.选出年龄小于22的
看了题目要求,我们有很多方法来实现,我们先看看最基础的foreach遍历,如下所示
//1.选出年龄大于21岁的 { foreach (student item in ListStu) { if (item.Age > 21) { result.Add(item); } } } //2.选出年龄大于20的 { foreach (student item in ListStu) { if (item.Age > 20) { result.Add(item); } } } //3.选出年龄小于22的 { foreach (student item in ListStu) { if (item.Age < 22) { result.Add(item); } } }
从以上代码,我们可以看出,要实现这三个要求,并不困难。不过我们仔细观察代码,这是典型的复制粘贴编码,我们的三个要求分别写了一个代码逻辑,这就会造成代码冗余,不便于今后维护。我们可以想想,现在才三个条件,随着需求的变更,我们的代码逻辑要不断复制粘贴修改,那有什么办法可以消除冗余呢?
观察以上代码,我们不难看出,三个foreach循环唯一的不同就是if条件逻辑不一样,其他都一样的。为了实现代码复用,我们此时可以考虑把if判断逻辑作为方法参数传入,利用委托就可以实现代码复用。为了增强代码的复用性,我们可以把类型泛型化。具体代码实现如下所示:
public static class ExtendClass { /// <summary> /// 泛型化方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="list"></param> /// <param name="fun"></param> /// <returns></returns> public static List<T> ThemeWhere<T>(this List<T> list,Func<T,bool> fun) { List<T> result = new List<T>(); foreach (T item in list) { bool b = fun.Invoke(item); if (b) { result.Add(item); } } return result; } }
在上述代码中,我们把类似if (item.Age > 21) 这样的条件逻辑,作为参数传递进去,在方法里面通过fun.Invoke(item)方法就可调用判断逻辑,实现代码复用。实现要求的三个条件,我们现在可以写成如下代码:
//1.选出年龄大于21岁的 var list1 = ListStu.ThemeWhere(p => p.Age > 21); //2.选出年龄大于20的 var list2 = ListStu.ThemeWhere(p => p.Age > 20); //3.选出年龄小于22的 var list3 = ListStu.ThemeWhere(p => p.Age < 22);
这样一来,代码是不是简介了很多,也便于今后维护,如果我们今后逻辑修改了,只需修改传入逻辑即可。
不难看出,ListStu.ThemeWhere(p => p.Age > 21);这个方法的使用是不是类似我们使用linq的where方法(ListStu.where(p => p.Age > 21));
通过反编译工具,我们可以看到LINQ的where方法定义如下所示(有改动):
public static IEnumerable<TSource> Where <TSource>(this IEnumerable<TSource> source,Func<TSource,bool> pre) { foreach (var item in source) { if (pre.Invoke(item)) { yield return item; //延迟查询(只有返回类型是IEnumerable,才可以用yield迭代器) } } }
是不是觉得很类似,只不过linq.where是通过yield迭代器返回结果的,注意只有返回类型是IEnumerable,才可以用yield迭代器。
使用迭代器yield和直接return有什么区别呢?
yield是延迟查询,使用到哪一个数据,就返回哪一个数据。
而直接return的话,是一次性查询出所有数据返回。
var listStu1 = ListStu.ThemeWhere(p=>p.Age>21); var listStu2 = ListStu.MyWhere(p => p.Age > 21);//foreach 每次循环才会调用where筛选,如果foreach里面终止循环,那where这里也不在进行筛选 foreach (var item in listStu1) { Console.WriteLine(string.Format(item.Name+","+item.Age+","+item.Sex)); Console.WriteLine(string.Format("**********************************")); } foreach (var item in listStu2) { Console.WriteLine(string.Format(item.Name+","+item.Age+","+item.Sex)); }
运行代码后我们会发现listStu1打印是一次性打印完成,listStu2打印是foreach循环一次,where执行一次筛选打印。这样的好处就是,我们在foreach里面break后,where查询不会再继续下去
注意:
Linq to object 出现在实现IEnumerable类中,只能操作内存数据,用的是Lamda方法。
3.Linq to Sql 简介
Linq to sql 主要出现在操作IQueryable数据,使用的是lanmda表达式目录树。操作的是数据库的数据,里面封装了ADO .Net的操作,不同的sql语句通过表达式目录树拼凑出来。通过传入lanmda表达式就能完成对数据库的操作。
Linq to sql 目前还在学习中,目前了解的只有这些
以上就是我对Linq学习的一些新得,如有不正确之处,望批评指正。