本系列的前两篇文章主要讲了C#3.0引入的新特性,也正是这些新特性让Linq成为可能。我先总结一下前面的知识点,我简单的列举出来:
■ Implicitly typed local variables:隐式类型局部变量
■ Object initializers:对象初始化器
■ Lambda expressions:Lambda表达式
■ Extension methods:扩展方法
■ Anonymous types:匿名类型
我简单的画了一张图来描述他们在Linq中扮演的角色:
这里我为什么又拿出来呢,因为在接下来的篇幅中,我们总会用到上面这些知识点,因为他们太重要了。好了,我们进入今天的主题:
这篇文章主要讲三个点:
- sequences
- query operators
- 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 }