Select操做符对单个序列或集合中的值进行投影。下面的示例中使用select从序列中返回Employee表的全部列:html
using (NorthwindDataContext db=new NorthwindDataContext())数组 {ide //查询语法函数 var query =this from e in db.Employeesspa where e.FirstName.StartsWith("M")code select e;orm //方法语法htm var q =对象 db.Employees .Where(e => e.FirstName.StartsWith("M")) .Select(e => e);
foreach (var item in query) { Console.WriteLine(item.FirstName); } } |
固然,你也能够返回单个列,例如:
var query = from e in db.Employees where e.FirstName.StartsWith("M") select e.FirstName; |
你也能够返回序列中的某几列,例如:
var query = from e in db.Employees where e.FirstName.StartsWith("M") select new { e.FirstName, e.LastName, e.Title }; |
SelectMany操做符提供了将多个from子句组合起来的功能,它将每一个对象的结果合并成单个序列。下面是一个示例:
using (NorthwindDataContext db=new NorthwindDataContext()) { //查询语法 var query = from e in db.Employees from o in e.Orders select o;
//方法语法 var q = db.Employees .SelectMany(e => e.Orders);
foreach (var item in query) { Console.WriteLine(item.Freight); } } |
Where是限制操做符,它将过滤标准应用在序列上,按照提供的逻辑对序列中的数据进行过滤。
Where操做符不启动查询的执行。当开始对序列进行遍历时查询才开始执行,此时过滤条件将被应用到查询中。Where操做符的使用方法已经在第一节中出现过,这里再也不冗述。
排序操做符,包括OrderBy、OrderByDescending、ThenBy、ThenByDescending和Reverse,提供了升序或者降序排序。
OrderBy操做符将序列中的元素按照升序排列。下面的示例演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext()) { //查询语法 var query = from e in db.Employees orderby e.FirstName select e;
//方法语法 var q = db.Employees .OrderBy(e => e.FirstName) .Select(e => e);
foreach (var item in q) { Console.WriteLine(item.FirstName); } } |
这里可使用OrderBy的重载方法OrderBy(Func<T,TKey>,IComparer<Tkey>)来指定序列的排序方式。
OrderByDescending操做符将序列中的元素按照降序排列。用法与OrderBy相同,这里再也不演示。
ThenBy操做符实现按照次关键字对序列进行升序排列。此操做符的查询语法与方法语法略有不一样,如下代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext()) { //查询语法 var query = from e in db.Employees orderby e.FirstName,e.LastName select e;
//方法语法 var q = db.Employees .OrderBy(e => e.FirstName) .ThenBy(e => e.LastName) .Select(e => e);
foreach (var item in query) { Console.WriteLine(item.FirstName); } } |
ThenByDescending操做符实现按照次关键字对序列进行降序排列。此操做符的查询语法与方法语法略有不一样,如下代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext()) { //查询语法 var query = from e in db.Employees orderby e.FirstName,e.LastName descending select e;
//方法语法 var q = db.Employees .OrderBy(e => e.FirstName) .ThenByDescending(e => e.LastName) .Select(e => e);
foreach (var item in query) { Console.WriteLine(item.FirstName); } } |
Reverse将会把序列中的元素按照从后到前的循序反转。须要注意的是,Reverse方法的返回值是void,如下代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext()) { //方法语法 var q = db.Employees .Select(e => e.FirstName) .ToList(); q.Reverse();
foreach (var item in q) { Console.WriteLine(item); } } |
联接是指将一个数据源对象与另外一个数据源对象进行关联或者联合的操做。这两个数据源对象经过一个共同的值或者属性进行关联。
LINQ有两个联接操做符:Join和GroupJoin。
Join操做符相似于T-SQL中的inner join,它将两个数据源相联接,根据两个数据源中相等的值进行匹配。例如,能够将产品表与产品类别表相联接,获得产品名称和与其相对应的类别名称。如下的代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中 //查询语法 var query = from p in db.Products join c in db.Categories on p.CategoryID equals c.CategoryID where p.CategoryID == 1 select p;
//方法语法 var q = db.Products .Join ( db.Categories, p => p.CategoryID, c => c.CategoryID, (p, c) => p ) .Where(p => p.CategoryID == 1);
foreach (var item in query) { Console.WriteLine(item.ProductName); } } |
以上代码为表述清晰加入了一个条件“where p.CategoryID == 1”,即仅返回产品类别ID为1的全部产品。
GroupJoin操做符常应用于返回“主键对象-外键对象集合”形式的查询,例如“产品类别-此类别下的全部产品”。如下的代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中 //查询语法 var query = from c in db.Categories join p in db.Products on c.CategoryID equals p.CategoryID into r select new { c.CategoryName, Products = r };
//方法语法 var q = db.Categories .GroupJoin ( db.Products, c => c.CategoryID, p => p.CategoryID, (c, p) => new { c.CategoryName, Products = p } );
foreach (var item in query) { Console.WriteLine("{0} =>", item.CategoryName); foreach (var p in item.Products) { Console.WriteLine(p.ProductName); } Console.WriteLine("----------------------------------------------"); } } |
分组是根据一个特定的值将序列中的元素进行分组。LINQ只包含一个分组操做符:GroupBy。
下面的示例中使用了产品表,以CategoryID做为分组关键值,按照产品类别对产品进行了分组。
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中 //查询语法 var query = from p in db.Products group p by p.CategoryID;
//方法语法 var q = db.Products .GroupBy(p => p.CategoryID);
foreach (var item in query) { Console.WriteLine("{0} =>", item.Key); foreach (var p in item) { Console.WriteLine(p.ProductName); } Console.WriteLine("----------------------------------------------"); } } |
执行GroupBy获得的序列中包含的元素类型为IGrouping<TKey?, T>,其Key属性表明了分组时使用的关键值,遍历IGrouping<TKey?, T>元素能够读取到每个T类型。在此示例中,对应的元素类型为IGrouping<int?, Products>,其Key属性即为类别ID,遍历它能够读取到每个产品对象。
串联是一个将两个集合联接在一块儿的过程。在LINQ中,这个过程经过Concat操做符来实现。
在下面的示例中,将会把类别名称串联在产品名称以后:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法 var q = db.Products .Select(p => p.ProductName) .Concat ( db.Categories.Select(c => c.CategoryName) );
foreach (var item in q) { Console.WriteLine(item); } } |
聚合函数将在序列上执行特定的计算,并返回单个值,如计算给定序列平均值、最大值等。共有7种LINQ聚合查询操做符:Aggregate、Average、Count、LongCount、Max、Min和Sum。
Aggregate操做符对集合值执行自定义聚合运算。例如,须要列出全部产品类别清单,每一个类别名称之间用顿号链接。如下的代码演示了这一过程:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法 var q = db.Categories .Select(c => c.CategoryName) .ToArray() .Aggregate((current, next) => String.Format("{0}、{1}", current, next));
Console.WriteLine(q); } |
若是你对这一过程有些迷惑,那么请参照如下代码:
var query = db.Categories .Select(c => c.CategoryName) .ToArray(); string r = String.Empty; foreach (var item in query) { r += "、"; r += item; } r = r.Substring(1); //去除第一个顿号 Console.WriteLine(r); |
求集合中元素的平均值,返回值类型double
求集合中元素的个数,返回值类型Int32
求集合中元素的个数,返回值类型Int64
求集合中元素的最大值
求集合中元素的最小值
求集合中元素的和
LINQ 中的集合操做符是指根据相同或不一样集合(或集)中是否存在等效元素来生成结果集的查询操做,一共有4种:
方法名 |
说明 |
Distinct |
从集合移除重复值。 |
Except |
返回差集,差集是指位于一个集合但不位于另外一个集合的元素。 |
Intersect |
返回交集,交集是指同时出如今两个集合中的元素。 |
Union |
返回并集,并集是指位于两个集合中任一集合的惟一的元素。 |
使用方式均为“集合1.方法名(集合2)”,返回值为运算结果的集合,这里就不演示了。
生成是指建立新的值序列。
Empty操做符返回一个指定类型的空集合。这里的空不是null,而是元素数量为0的集合。如下的示例演示了如何建立一个IEnumerable<int>类型的空集合:
var q = Enumerable.Empty<int>(); Console.WriteLine(q == null); Console.WriteLine(q.Count()); |
DefaultIfEmpty将空集合替换为具备默认值的单一实例集合。执行此方法得到的集合将至少含有一个元素,这是由于DefaultIfEmpty方法须要两个参数,第一个参数是一个泛型集合,第二个参数是相应类型的单个元素,若是第一个参数中不含有任何元素,它将返回第二个参数指定的单个元素。若是你使用了DefaultIfEmpty方法的重载方法DefaultIfEmpty<T>(IEnumerable<T> array),若是指定的array集合为空,那么将返回一个类型为T,值为null的单个对象。如下的代码演示了这一过程:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法 var q = Enumerable.DefaultIfEmpty ( db.Employees .Where(e => e.FirstName.StartsWith("Aaf")) //更改此处的条件可得到不一样的集合 , new Employees() { FirstName = "Sunny D.D" } ); Console.WriteLine(q.Count()); foreach (var item in q) { Console.WriteLine(item.FirstName); } } |
Range操做符用于生成指定范围内的整数的序列。它须要两个参数,第一个参数是序列开始的整数值,第二个参数是序列中整数的数量。下面的示例演示了使用Range操做符来生成从0到9的整数序列:
var q = Enumerable.Range(0, 10); foreach (var item in q) { Console.WriteLine(item); } |
Repeat操做符用于生成包含一个重复值的集合。它须要两个参数,第一个参数是任意类型的元素,第二个参数是生成的序列中所包含此元素的数量。下面的示例演示了使用Repeat来生成一个包含10个0的序列:
var q = Enumerable.Repeat(0, 10); foreach (var item in q) { Console.WriteLine(item); } |
转换操做符是用来实现将输入对象的类型转变为序列的功能。名称以“As”开头的转换方法可更改源集合的静态类型但不枚举(延迟加载)此源集合。名称以“To”开头的方法可枚举(即时加载)源集合并将项放入相应的集合类型。
全部实现了IEnumerable<T>接口的类型均可以调用此方法来获取一个IEnumerable<T>集合。此方法通常仅用于实现类中的方法与IEnumerable<T>接口方法重名时。例如,实现类Test中有一个Where方法,当使用Test对象调用Where时,将执行Test自身的Where方法过程。若是要执行IEnumerable<T>的Where方法,即可以使用AsEnumerable进行进行转换后,再调用Where方法便可。固然,将实现类Test隐式转换为IEnumerable<T>接口,再调用接口的Where方法也能达到一样的效果。如下的代码演示了这一过程:
class AsEnumerableTest<T> : List<T> { public void Where(Func<T, bool> func) { Console.WriteLine("AsEnumerableTest的Where方法"); } } public static void AsEnumerable() { AsEnumerableTest<int> q = new AsEnumerableTest<int>() { 1,2,3,4 }; q.Where(r => r < 3);
//q.AsEnumerable().Where(r => r < 3);
//IEnumerable<int> i = q; //i.Where(r => r < 3); } |
Cast<T> 方法经过提供必要的类型信息,可在IEnumerable(非泛型)的派生对象上调用Cast<T> 方法来得到一个IEnumerable<T>对象。例如,ArrayList 并不实现IEnumerable<T>,但经过调用 ArrayList 对象上的 Cast<T>(),就可使用标准查询运算符查询该序列。
若是集合中的元素没法强制转换为 T 类型,则此方法将引起异常。如下代码演示了这一过程:
ArrayList array = new ArrayList(); array.Add("Bob"); array.Add("Jack"); array.Add(1); foreach (var item in array.Cast<string>()) { Console.WriteLine(item); } |
运行此代码,能够输出“Bob”、“Jack”,而后会报出一个异常“没法将int强制转换为string”,这说明Cast方法也是延迟执行实现的,只有在枚举过程当中才将对象逐个强制转换为T类型。
OfType <T> 方法经过提供必要的类型信息,可在IEnumerable(非泛型)的派生对象上调用OfType <T> 方法来得到一个IEnumerable<T>对象。执行OfType<T>方法将返回集合中强制转换类型成功的全部元素。也就是说,OfType<T>方法与Cast<T> 方法的区别在于,若是集合中的元素在强制转换失败的时候会跳过,而不是抛出异常。
ToArray 操做符能够在IEnumerable<T> 类型的任何派生对象上调用,返回值为T类型的数组。
ToDictionary操做符根据指定的键选择器函数,从IEnumerable<T>建立一个Dictionary<TKey, TValue>。下面的示例中,将查询到的产品类别集合转换为Dictionary<类别ID,类别名称>的键-值集合:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中 //方法语法 var q = db.Categories .ToDictionary ( c => c.CategoryID, c => c.CategoryName );
foreach (var item in q) { Console.WriteLine("{0} - {1}",item.Key,item.Value); } } |
须要注意的是,若是省略ToDictionary方法的第二个参数(值选择函数),那么Value将会保存一个类别对象。还有,若是Key为null,或者出现重复的Key,都将致使抛出异常。
ToList操做符能够在IEnumerable<T> 类型的任何派生对象上调用,返回值为List<T>类型的对象。
ToLookup操做符将建立一个 Lookup<TKey, TElement>对象,这是一个one-to-many集合,一个Key能够对应多个Value。如下的示例以产品表的全部数据做为数据源,以类别ID做为Key调用了ToLookup方法,而后遍历返回的Lookup<TKey, TElement>对象,输出了类别ID以及此类别下的全部产品名称:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中 //方法语法 var q = db.Products .ToLookup ( p => p.CategoryID, p => p.ProductName );
foreach (var item in q) { Console.WriteLine(item.Key); foreach (var p in item) { Console.WriteLine(p); } } } |
能够看出,ToLookup操做与GroupBy操做很类似,只不过GroupBy是延迟加载的,而ToLookup是即便加载。
元素操做符将从一个序列中返回单个指定的元素。
First操做将返回序列中的第一个元素。若是序列中不包含任何元素,则First<T>方法将引起异常。若要在源序列为空时返回默认值,须要使用FirstOrDefault方法。如下代码演示了First<T>方法的使用方式:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中 //无参 var query = db.Employees .First();
//有参 var q = db.Employees .First(e => e.FirstName.StartsWith("S"));
Console.WriteLine(q.FirstName); } |
上述代码中使用了First<T>方法的无参方式与有参方式。First<T>的有参方式中能够指定一个条件,操做将返回序列中知足此条件的第一个元素。从查询结果上看,source.First<T>(条件)方法与source.Where(条件).First<T>()是同样的,可是须要注意“First<T>(条件)操做将返回序列中知足此条件的第一个元素”,这将忽略后面的遍历操做,效率更高。
FirstOrDefault方法将返回序列中的第一个元素;若是序列中不包含任何元素,则返回默认值。它也能够像First方法同样传递一个条件。须要说明的是若是序列中不包含任何元素,返回的默认值是个怎样的元素。在这以前,先来看一下FirstOrDefault方法是如何实现的:
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source) { if (source == null) { throw Error.ArgumentNull("source"); } IList<TSource> list = source as IList<TSource>; if (list != null) { if (list.Count > 0) { return list[0]; } } else { using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } } return default(TSource); } |
如下给出MSDN中,对于default(T)关键字的描述:
在泛型类和泛型方法中产生的一个问题是,在预先未知如下状况时,如何将默认值分配给参数化类型 T:
给定参数化类型 T 的一个变量 t,只有当 T 为引用类型时,语句 t = null 才有效;只有当 T 为数值类型而不是结构时,语句 t = 0 才能正常使用。解决方案是使用 default 关键字,此关键字对于引用类型会返回 null,对于数值类型会返回零。对于结构,此关键字将返回初始化为零或 null 的每一个结构成员,具体取决于这些结构是值类型仍是引用类型。 |
Last方法将返回序列中的最后一个元素。使用方法参照First。
LastOrDefault方法将返回序列中的最后一个元素;若是序列中不包含任何元素,则返回默认值。使用方法参照FirstOrDefault。
ElementAt方法返回序列中指定索引处的元素。使用方法参照First。须要注意的是若是索引超出范围会致使异常。
ElementAtOrDefault方法将返回序列中指定索引处的元素;若是索引超出范围,则返回默认值。使用方法参照FirstOrDefault。
Single方法的无参形式将从一个序列中返回单个元素,若是该序列包含多个元素,或者没有元素数为0,则会引起异常。也就是说,在序列执行Single方法的无参形式时,必须保证该序列有且仅有一个元素。
Single方法的有参形式将从一个序列中返回符合指定条件的惟一元素,若是有多个元素,或者没有元素符合这一条件,则会引起异常。如下代码演示了Single的使用方式:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中 //方法语法 var q = db.Employees .Single();
var query = db.Employees .Single(e => e.FirstName.StartsWith("S")); Console.WriteLine(query.FirstName); } |
SingleOrDefault方法的无参形式将从一个序列中返回单个元素。若是元素数为0,则返回默认值。若是该序列包含多个元素,则会引起异常。
SingleOrDefault方法的有参形式将从一个序列中返回符合指定条件的惟一元素,若是元素数为0,则返回默认值;若是该序列包含多个元素,则会引起异常。SingleOrDefault的使用方式与Single相同。
须要注意的是,Single方法与SingleOrDefault方法都是即时加载的,在代码进行到方法所在位置时,若是引起了异常,会马上抛出。
若是两个序列的对应元素相等且这两个序列具备相同数量的元素,则视这两个序列相等。
SequenceEqual方法经过并行地枚举两个数据源并比较相应元素来判断两个序列是否相等。若是两个序列彻底相等,返回true,不然返回false。如下代码是SequenceEqual方法的实现过程:
public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second,IEqualityComparer<TSource> comparer) { if (comparer == null) { comparer = EqualityComparer<TSource>.Default; } if (first == null) { throw Error.ArgumentNull("first"); } if (second == null) { throw Error.ArgumentNull("second"); } using (IEnumerator<TSource> enumerator = first.GetEnumerator()) { using (IEnumerator<TSource> enumerator2 = second.GetEnumerator()) { while (enumerator.MoveNext()) { if (!enumerator2.MoveNext() || !comparer.Equals(enumerator.Current, enumerator2.Current)) { return false; } } if (enumerator2.MoveNext()) { return false; } } } return true; } |
以上代码的执行过程为:
限定符运算返回一个 Boolean 值,该值指示序列中是否有一些元素知足条件或是否全部元素都知足条件。
下图描述了两个不一样源序列上的两个不一样限定符运算。第一个运算询问是否有一个或多个元素为字符“A”,结果为 true。第二个运算询问是否全部元素都为字符“A”,结果为true。
All方法用来肯定是否序列中的全部元素都知足条件。如下代码演示了All的用法:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" }; string[] source2 = new string[] { "A", "A", "A", "A", "A", "A" };
Console.WriteLine(source1.All(w => w == "A")); //console will print "False" Console.WriteLine(source2.All(w => w == "A")); //console will print "True" |
Any方法的无参方式用来肯定序列是否包含任何元素。若是源序列包含元素,则为 true;不然为 false。
Any方法的有参方式用来肯定序列中是否有元素知足条件。只要有一个元素符合指定条件即返回true,若是一个符合指定条件的元素都没有则返回false。如下代码演示了Any方法有参方式的用法:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" }; string[] source2 = new string[] { "A", "A", "A", "A", "A", "A" };
Console.WriteLine(source1.Any(w => w == "A")); //console will print "True" Console.WriteLine(source2.Any(w => w == "A")); //console will print "True" |
Contains方法用来肯定序列是否包含知足指定条件的元素。若是有返回true,不然返回false。如下代码使用默认的String比较器来判断序列中是否含有指定的字符串:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" };
Console.WriteLine(source1.Contains("A")); //console will print "True" Console.WriteLine(source1.Contains("G")); //console will print "False" |
若是要对序列中的元素进行自定义比较,须要一个IEqualityComparer<T>接口的实现类做为比较器,用于比较序列中的元素。
LINQ 中的分区指的是在不从新排列元素的状况下,将输入序列划分为两部分,而后返回其中一个部分的操做。
下图显示对一个字符序列执行三个不一样的分区操做的结果。第一个操做返回序列中的前三个元素。第二个操做跳过前三个元素,返回剩余的元素。第三个操做跳过序列中的前两个元素,返回接下来的三个元素。
Take(int n)方法将从序列的开头返回数量为n的连续元素。如下代码演示了从一个序列中返回其前五个元素:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 }; var q = source.Take(5); foreach (var item in q) { Console.WriteLine(item); } |
上述代码的运行结果为下图所示:
TakeWhile方法执行时将逐个比较序列中的每一个元素是否知足指定条件,直到碰到不符合指定的条件的元素时,返回前面全部的元素组成的序列。如下代码演示了这一过程:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 }; var q = source.TakeWhile(i => i < 100); foreach (var item in q) { Console.WriteLine(item); } |
上述代码的运行结果为下图所示:
Skip(int n)方法将跳过序列开头的n个元素,而后返回其他的连续元素。如下代码演示了从一个序列中跳过前五个元素,而后返回其他的元素组成的序列:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 }; var q = source.Skip(5); foreach (var item in q) { Console.WriteLine(item); } |
上述代码的运行结果为下图所示:
SkipWhile方法执行时将逐个比较序列中的每一个元素是否知足指定条件,直到碰到不符合指定的条件的元素时,返回其他全部的元素组成的序列。如下代码演示了这一过程:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 }; var q = source.SkipWhile(i => i < 100); foreach (var item in q) { Console.WriteLine(item); } |
上述代码的运行结果为下图所示:
本文介绍了LINQ标准查询操做符。没有这些操做符,LINQ就不会存在。本文为理解这些操做符的功能提供了很好的基础。了解它们将会颇有帮助,由于LINQ的各类Provider都是基于这些操做符来完成各自丰富的功能。