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学习的一些新得,如有不正确之处,望批评指正。

posted @ 2019-10-08 12:33  dont~  阅读(271)  评论(0编辑  收藏  举报