LINQ to Objects---延时执行的Enumerable类方法
LINQ标准查询运算法是依靠一组扩展方法来实现的。而这些扩展方法分别在System.Linq.Enumerable和System.Linq.Queryable这连个静态类中定义。
Enumerable的扩展方法采用线性流程,每个运算法会被线性执行。这种执行方法如果操作类似关系型数据库数据源,效率会非常低下,所以Queryable重新定义这些扩展方法,把LINQ表达式拆解为表达式树,提供程序就可以根据表达式树生成关系型数据库的查询语句,即SQL命令,然后进行相关操作。
每个查询运算符的执行行为不同,大致分为立即执行和延时执行。延时执行的运算符将在枚举元素的时候被执行。
Enumerable类位于程序集System.Core.dll中,System.Linq命名空间下,并且直接集成自System.Object,存在于3.5及以上的.NET框架中。Enumerable是静态类,不能实例化和被继承,其成员只有一组静态和扩展方法。
LINQ不仅能够查询实现IEnumerable<T>或IQueryable<T>的类型,也能查询实现IEnumerable接口的类型。关于Enumerable方法的详细说明,请参考MSDN Enumerable 类
“--------------------------------------------------------------
理解LINQ首先必须理解扩展方法
msdn是这样规定扩展方法的:“扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。 它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀。”
下面给个扩展方法的例子如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 扩展方法 { /// <summary> /// 为string类型定义一个扩展方法 /// </summary> static class Helper { public static string MyExtenMethod(this string s) { return s.Substring(0, 2); } } class Program { static void Main(string[] args) { string s = "扩展方法示例"; Console.WriteLine(s.MyExtenMethod());//调用 Console.ReadKey(false); } } }
程序的运行结果如下:
-----插曲,想到了就加进来,有助于理解开头的几段话及LINQ原理
参考DebugLZQ前面的博文:浅析EF涉及的一些C#语言特性
---------------------------------------------------------------”
为了方便理解和记忆,DebugLZQ将常用的延时执行的Enumerable类方法成员分了下组,具体如下:
1.Take用于从一个序列的开头返回指定数量的元素
2.TakeWhile 用于获取指定序列从头开始符合条件的元素,直到遇到不符合条件的元素为止
3.Skip跳过序列中指定数量的元素
4.SkipWhile 用于跳过序列总满足条件的元素,然会返回剩下的元素
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 延时执行的Enumerable类方法 { /// <summary> /// 延时执行的Enumerable类方法 /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { string[] names = { "DebugLZQ","DebugMan","Sarah","Jerry","Tom","Linda","M&M","Jeffery"}; //1.Take用于从一个序列的开头返回指定数量的元素 // //a.在数组上直接使用Take方法 foreach (string name in names.Take(3)) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); //b.在LINQ返回的IEnumerable<T>序列上使用Take方法 var query = from string name in names where name.Length <=3 select name; foreach (string name in query.Take(1)) { Console.Write("{0} ",name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); //2.TakeWhile 用于获取指定序列从头开始符合条件的元素,直到遇到不符合条件的元素为止 // var takenames = names.TakeWhile(n => n.Length>4); var takenames2 = names.TakeWhile((n,i)=>n.Length<10&&i<3); foreach (string name in takenames) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); foreach (string name in takenames2) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); //3.Skip跳过序列中指定数量的元素 // foreach (string name in names.Skip(5)) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); var query_skip = (from name in names where name.Length >= 3 select name).Skip(2); foreach (string name in query_skip.Skip(2) ) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); //4.SkipWhile 用于跳过序列总满足条件的元素,然会返回剩下的元素 //跳过名字长度大于3的 var takenames_SkipWhile = names.SkipWhile(n => n.Length >3); foreach (string name in takenames_SkipWhile) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); var takenames_SkipWhile2 = names.SkipWhile((n,i)=>n.Length>3&&i>2); foreach (string name in takenames_SkipWhile2) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); //小结Take、Skip获得第N到第M个元素 var names_TakeAndSkip = names.Skip(5).Take(3); var names_TakeAndSkip2 = (from name in names select name).Skip(5).Take(3); foreach (string name in names_TakeAndSkip) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("-----"); foreach (string name in names_TakeAndSkip2) { Console.Write("{0} ", name); } Console.WriteLine(); Console.WriteLine("----------------------------"); Console.ReadKey(false); } } }
程序中有详细的注释不再多做说明,程序运行结果如下:
5.Reverse用于翻转序列中的元素的顺序
6.Distinct过滤掉重复的元素
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Reverse_Distinct等 { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { string[] names = { "DebugLZQ", "Jerry", "Sarah", "Jerry", "Tom", "Linda", "M&M", "Jeffery" }; //5.Reverse用于翻转序列中的元素的顺序 string str = "反转字符串"; var strre = str.ToCharArray().Reverse(); var takenames = names.Reverse(); foreach (var c in strre) { Console.Write(c); } Console.WriteLine(); Console.WriteLine("-----"); foreach (var c in takenames ) { Console.WriteLine(c); } Console.WriteLine("----------------------------"); Console.ReadKey(false); //6.Distinct 过滤掉重复的元素 var takenames_Distinct = names.Distinct(); foreach (var c in takenames_Distinct) { Console.WriteLine(c); } Console.WriteLine("----------------------------"); Console.ReadKey(false); } } }
程序的运行结果如下:
7.Union用于合并两个序列,并去掉重复项
8.Concat用于连接两个序列,不会去掉重复项
9.Intersect用于获得连个序列的交集
10.Except用于获得两个结合的差集
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Union_Concat_Intersect_Except { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { string[] names1 = { "DebugLZQ", "Jerry", "Sarah", "Jerry", "Tom", "Linda", "M&M", "Jeffery" }; string[] names2 = { "DebugLZQ", "Jerry", "Sarah" }; //7.Union用于合并两个序列,并去掉重复项 var names_Union = names1.Union(names2); //8.Concat用于连接两个序列,不会去掉重复项 var names_Concat = names1.Concat(names2); //9.Intersect用于获得连个序列的交集 var names_Intersect = names1.Intersect(names2); //10.Except用于获得两个结合的差集 var names_Except = names1.Except(names2); foreach (string name in names_Union) { Console.WriteLine(name); } Console.WriteLine("-----"); Console.ReadKey(false); foreach (string name in names_Concat) { Console.WriteLine(name); } Console.WriteLine("-----"); Console.ReadKey(false); foreach (string name in names_Intersect) { Console.WriteLine(name); } Console.WriteLine("-----"); Console.ReadKey(false); foreach (string name in names_Except) { Console.WriteLine(name); } Console.WriteLine("-----"); Console.ReadKey(false); } } }
程序的运行结果如下:
11.Range 用于生成指定范围内的“整数”序列
12.Repeat用于生成指定数量的重复元素
13.Empty 用于获得一个指定类型的空序列
14.DefaultIfEmpty 用于获得序列,如果为空,则添加一个默认类型元素
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Range_Empty_DefalultIfEmpty { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { //11.Range 用于生成指定范围内的“整数”序列 var num2 = Enumerable.Range(10, 15); //12.Repeat用于生成指定数量的重复元素 var guest = new {Name="橙子",Age=25 }; var Guests = Enumerable.Repeat(guest, 5); //13.Empty 用于获得一个指定类型的空序列 var empty = Enumerable.Empty<string>(); //14.DefaultIfEmpty 用于获得序列,如果为空,则添加一个默认类型元素 //a var intempty = Enumerable.Empty<int>(); Console.WriteLine(intempty.Count()); Console.WriteLine("-----------"); foreach (var n in intempty) { Console.WriteLine(n); } Console.WriteLine("-----------"); Console.WriteLine(intempty.DefaultIfEmpty().Count()); Console.WriteLine("-----------"); foreach (var n in intempty.DefaultIfEmpty()) { Console.WriteLine(n); } Console.WriteLine("--------------------------"); Console.ReadKey(false); //b string[] names = { "DebugLZQ", "DebugMan", "Sarah", "Jerry", "Tom", "Linda", "M&M", "Jeffery" }; var query = from name in names where name == "LBJ" select name; Console.WriteLine(query.Count()); Console.WriteLine(query.DefaultIfEmpty().Count());//默认为null foreach (var n in query.DefaultIfEmpty()) { Console.WriteLine(n); } Console.WriteLine("---------------"); Console.ReadKey(false); //c指定一个默认值 foreach (var n in intempty.DefaultIfEmpty(100)) { Console.WriteLine(n); } Console.WriteLine("--------------------------"); Console.ReadKey(false); foreach (var n in query.DefaultIfEmpty("James")) { Console.WriteLine(n); } Console.ReadKey(false); } } }
程序的运行结果如下:
15.OfType筛选指定类型的元素
16.Cast类型转换
17.AsEnumerable有些数据源类型不支持Enumerable的部分查询关键字,需要转换下,譬如IQueryable
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; namespace Cast_OfType_AsEnumerable { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { ArrayList names = new ArrayList(); names.Add("DebugLZQ"); names.Add("Jerry"); names.Add(100); names.Add(new {Name="LZQ",Age=26}); names.Add(new Stack()); //15.OfType筛选指定类型的元素 var takenames = names.OfType<string>(); //16.Cast类型转换 var takenames2 = names.OfType<string>().Cast<string>(); //17.AsEnumerable var takenames3 = takenames2.AsEnumerable(); foreach (var name in takenames3) { Console.Write("{0} ",name); } Console.ReadKey(false); } } }
程序运行结果如下:
延时执行,顾名思义就是不是立即执行,即不是在查询语句定义的时候执行,而是在处理结果集(如遍历)的时候执行,在Enumerable类方法成员中,除了本节总结的这常用的17个外,前面博文---LINQ基本子句 中总结的8个基本子句也都是延时执行的。注意延时执行的查询程序的执行流程。