代码改变世界

Linq学习之路(04) - Linq基础揭秘

2013-05-18 14:06  ARMdong  阅读(1054)  评论(0编辑  收藏  举报

本系列的前两篇文章主要讲了C#3.0引入的新特性,也正是这些新特性让Linq成为可能。我先总结一下前面的知识点,我简单的列举出来:

■ Implicitly typed local variables:隐式类型局部变量
■ Object initializers:对象初始化器
■ Lambda expressions:Lambda表达式
■ Extension methods:扩展方法
■ Anonymous types:匿名类型

我简单的画了一张图来描述他们在Linq中扮演的角色:

这里我为什么又拿出来呢,因为在接下来的篇幅中,我们总会用到上面这些知识点,因为他们太重要了。好了,我们进入今天的主题:

这篇文章主要讲三个点:

  1. sequences
  2. query operators
  3. query expressions

这里用英文原句来介绍,因为翻译成中文,味道就变了。

好,开始我们今天的第一个知识点:

一:Sequences

首先我们要搞清楚,什么类型的sequence才能被Linq查询,通过我们之前的例子来说事:

var processes = Process.GetProcesses()
    .Where(process => process.WorkingSet64 >= 1024 * 1024 * 10)
    .OrderByDescending(process => process.WorkingSet64)
    .Select(process => new
    {
        process.Id,
        Name = process.ProcessName,
        Memory = process.WorkingSet64
    });

这段代码中,GetProcesses()方法返回的是一组Process对象的数组。在.net中,array实现了IEnumerable<T>的泛型接口,而GetProcesses方法是定义在System.Diagnostics.Process的类中,Process类实现了IEnumerable<Process>接口,所以我们暂时得到的结论是,若想使用linq query,那么我们的对象就必须实现IEnumerable<T>接口。IEnumerable<T>接口非常重要,上例中我们用到的Where、OrderByDescending、Select等扩展方法都是使用Process类型的对象作为参数。

另外补充一点,Linq还有一个重要的特性就是Deferred query execution(延时执行),也就是说我们通过一些linq query expression或linq query operation操作获得的sequence,并没有立即到objects或数据库中把这些对象拿出来,而是等我们真正用到的时候才取出来,有个最典型的应用就是“按需加载”,对我们有利用价值的东西我们取出来,没用到的就不取,这样会降低我们程序的压力,提高性能。这里我通过一个简单的例子来说明一下延时加载:

首先,我们不用linq查询来实现输出一个整型数组里每个元素的平方:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace DeferredQueryExecution
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             int[] nums = { 1, 2, 3 };
13 
14             double[] query = new double[3];
15 
16             //给query赋值
17             for (int i = 0, len = nums.Length; i < len; i++)
18             {
19                 query[i] = Square(nums[i]);
20             }
21 
22             foreach (var n in query)
23             {
24                 Console.WriteLine(n);
25             }
26 
27             Console.ReadKey();
28         }
29 
30         /// <summary>
31         /// 计算num平方的方法
32         /// </summary>
33         /// <param name="num"></param>
34         /// <returns></returns>
35         static double Square(double num)
36         {
37             Console.WriteLine("计算平方值( " + num + " )...");
38             return Math.Pow(num, 2);
39         }
40     }
41 }
普通查询

输出结果:

再来看看用Linq查询的示例:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace DeferredQueryExecution
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             int[] nums = { 1, 2, 3 };
13 
14             var query =
15                     from num in nums
16                     select Square(num); 
17 
18             foreach (var n in query)
19             {
20                 Console.WriteLine(n);
21             }
22 
23             Console.ReadKey();
24         }
25 
26         /// <summary>
27         /// 计算num平方的方法
28         /// </summary>
29         /// <param name="num"></param>
30         /// <returns></returns>
31         static double Square(double num)
32         {
33             Console.WriteLine("计算平方值( " + num + " )...");
34             return Math.Pow(num, 2);
35         }
36     }
37 }
Linq查询

输出结果:

这里,我们可以很清楚的看出,使用linq查询并没有立即执行,而是当我们遍历query结果的时候才会真正的执行,这就是延时执行。

好,进入今天的第二个重点:query operations

 

二:Query Operations

什么是query operations?其实query operations就是IEnummerable<T>中给我定义的一些对于类型T的扩展方法,我列举一下常用的query operations。

 
Filtering OfType,Where
Projection Select,SelectMany
Partioning Skip,SkipWhile,Take,TakeWhie
Join   GroupJoin,Join
Concatenation Concate
Ordering OrderBy,OrderByDescending,Reverse,ThenBy,ThenByDescending
Grouping GroupBy,ToLookup
Set Distinct,Except,Intersect,Union
Conversion AsEnumerable,AsQueryable,Cast,ToArray,ToDictionary,ToList
Equality SequenceEqual
Element ElementAt,ElementAtOrDefault,First,FirstOrDefault,Last,LastOrDefault,Single,SingleOrDefault
Generation DefaultIfEmpty,Empty,Range,Repeat
Quantifiers All,Any,Contains
Aggregation Aggregate,Average,Count,LongCount,Max,Min,Sum

 

 

 

 

 

 

 

 

 

 

 

 

 

通过这些扩展方法,我们很容易对sequences进行一系列的操作,这篇文章中不会一一对这些方法进行介绍,但是后续文章中或多或少都会涉及到这些方法。其实上文中我们已经用到了一些query operation,例如Where、Select和OrderByDescending,根据这些方法的名字我们大概都能知道他们的功能。Where其实起到一个过滤的作用,把满足条件的对象筛选出来,OrderBy和OrderByDescending方法是对Select取出来的sequence按照传进去的参数进行排序。我们可以把这些方法看作是工厂中的一系列的流水线,将original的sequences输进去进行加工,然后得到我们需要的sequences,这里我贴出一张图方便大家理解:

 

三:Query expressions

Linq还为我们提供了第二种查询方法,就是query expression的方式,在某些情况下可能query operation并不是显得那么清楚,对于熟悉T-SQL的朋友们来说,query expression可能更容易理解,本人也偏向于query expression这种方式,但是有些时候,有些query operation也不能被很好的转换为query expression,好了,这些概念型的东西我就不说了,我也说不好,我们直接看代码吧,还是上文中的例子,下面我用query expression的方法实现:

var processes =
    from process in Process.GetProcesses()
    where process.WorkingSet64 >= 1024 * 1024 * 10
    orderby process.WorkingSet64 descending
    select new
        {
            process.Id,
            Name = process.ProcessName,
            Memory = process.WorkingSet64
        };

 

哈哈,这样看起来是不是更简单呢,更偏向于我们的思维呢?