这篇文章主要讨论LINQ中常用的标准查询操作符,为以后学习更多的标准查询操作符打下基础,同时会给出等同的查询表达式,下面是本篇的内容:
一、投影操作符:Select和SelectMany
1. Select
之前的两篇文章中已经多次用到Select操作符,该操作符是LINQ查询中最基本也是最重要的操作符之一,它根据传入的参数在某一序列上执行投影操作,声明如下:
public static IEnumerable<S> Select<T, S> (
this IEnumerable<T> source,
Func<T, S> selector);
示例如下:
var companies = customers.Select(customer => customer.Company);//companies为IEnumerable<T>类型
等同的查询表达式:
var companies = from customer in customers
select customer.Company;
在Select操作符返回的结果集中,其中的元素既可以是原来传入的对象,也可以是包含该元素若干字段的新对象
比如上面的例子可以写成下面这样,用来返回一个匿名对象:
var custInfos = customers.Select(customer => new { customer.FirstName, customer.LastName, customer.Company });
//查询表达式
//from customer in customers
//select new { customer.FirstName, customer.LastName, customer.Company };
说明:LINQ标准查询操作符均作为扩展方法定义在 System.Linq.Enumerable 类中
2. SelectMany
先来看 SelectMany 的声明:
public static IEnumerable<S> SelectMany<T, S> (
this IEnumerable<T> source,
Func<T, IEnumerable<S>> selector);
SelectMany操作符将把由selector函数返回序列中的各个集合元素中的值连接起来,并映射成一个新的序列
例如之前这篇“LINQ解析:LINQ to Objects简介”中的ASP.NET示例,我们只是查找出首要的作者,如果我们需要查找出所有的作者,使用Select操作符需要像下面这样:
books.Select(book => new { book.Authors });
//查询表达式
//from book in books
//select new { book.Authors };
以上代码返回的是IEnumerable<IEnumerable<string>>类型,但是我们要返回IEnumerable<string>类型,这就要用到SelectMany操作符:
books.SelectMany(book => book.Authors);
//查询表达式
//from book in books
//from author in book.Authors
//select author;
SelectMany操作符在查询表达式中可以用两个from子句表示,编译后则会变成对SelectMany操作符的调用
3. 查询索引
Select和SelectMany操作符能够得到序列中该元素的索引位置,同样使用前面的ASP.NET示例,改写查询语句如下:
books.Select((book, index) => new { Index = index, book.Title, book.Price })
.OrderBy(book => book.Price);
运行效果如下图所示:
说明:该代码没有等同的查询表达式,因为查询表达式没有表示带有索引的Select操作符
二、约束操作符 Where
之前这篇“LINQ解析:LINQ to Objects简介”中多次使用了该操作符,且该操作符比较简单,这里不作说明
注意:Where操作符同样有查询索引的重载
三、Distinct操作符
查询中难免会出现一些重复元素,这时候就要使用Distinct操作符来移除重复项;比如我们要看重要客户所在的城市,如果不使用Distinct操作符,我们可能会像下面这样查询结果:
Sample.Cutomers.Where(customer => customer.Level >= 5).Select(customer => customer.City);
//等效的查询表达式
//from customer in Sample.Cutomers
//where customer.Level >= 5
//select customer.City;
输出如下:
London
Paris
London
Paris
Paris
San Francisco
可以看到London重复了两次,Paris重复了三次,而我们不需要这些重复数据,只需要加个Distinct操作符就行:
Sample.Cutomers.Where(customer => customer.Level >= 5).Select(customer => customer.City).Distinct();
//C#中没有与Distinct等同的查询表达式语法
输出如下:
London
Paris
San Francisco
注:如果元素实现了IEquatable<T>接口,为了比较结果中的两个元素,Distinct操作符将使用元素的 IEquatable<T>.Equals方法;如果元素没有实现IEquatable<T>接口,则使用其 Object.Equals方法进行比较
测试所用的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
public static void Main()
{
var impCities = Sample.Cutomers
.Where(customer => customer.Level >= 5)
.Select(customer => customer.City)
.Distinct();
ObjectDumper.Write(impCities);
}
}
class Sample
{
public static Customer[] Cutomers = new Customer[]
{
new Customer("Maria Anders","Berlin",3),
new Customer("Victoria Ashworth","London",5),
new Customer("Dominique Perrier","Paris",7),
new Customer("Jytte Petersen","Kobenhavn",2),
new Customer("Karl Jablonski","Seattle",1),
new Customer("Steven Xue","Shanghai",3),
new Customer("Elizabeth Brown","London",6),
new Customer("Georg Pipps","Paris",8),
new Customer("Fran Wilson","Paris",5),
new Customer("Jaime Yorres","San Francisco",9)
};
}
class Customer
{
public Customer(String name, String city, UInt16 level)
{
Name = name;
City = city;
Level = level;
Company = "";
Phone = "";
}
public String Name { get; set; }
public String Company { get; set; }
public String Phone { get; set; }
public String City { get; set; }
//客户等级,等级越高说明客户越重要
public UInt16 Level { get; set; }
}
四、转换操作符
常用的转换操作符包括ToArray、ToList和ToDictionary等,使用起来比较简单,改写上面的例子,示例如下:
//Distinct、ToArray、ToList、ToDictionary操作符都会引起立即执行
IEnumerable<String> cities = Sample.Cutomers.Select(customer => customer.City).Distinct();
String[] array = cities.ToArray();
ObjectDumper.Write(array);
Console.WriteLine();
List<String> list = cities.ToList();
ObjectDumper.Write(list);
Console.WriteLine();
Dictionary<String, Customer> nameRef = Sample.Cutomers.ToDictionary(customer => customer.Name);
Customer customerByName = nameRef["Maria Anders"];
Console.Write("Customer found: ");
ObjectDumper.Write(customerByName);
注:Distinct、ToArray、ToList和ToDictionary能够立即得到查询结果,在调用时,这些操作符将完整地遍历源序列,以便得到构造集合对象所必需的所有数据
五、聚集操作符
用于在相关数据上进行算术运算,包括Count、Sum、Min和Max等
沿用“LINQ解析:LINQ to Objects简介”中的ASP.NET示例,改写如下:
var minPrice = books.Min(book => book.Price);
var maxPrice = books.Select(book => book.Price).Max();
var totalPrice = books.Sum(book => book.Price);
var totalPrice2 = books.Select(book => book.Price).Sum();
var dearBooksCount = books.Where(book => book.Price > 80).Count();
可以看到Min和Max的调用方式有所不同,Sum的两次调用也不同,不过结果却是一致的;在第一种方式中,聚集函数只应用到源序列中的某个属性上,在第 二种方式中,聚集函数应用在源序列的整个元素上;之所以Min、Max和Sum操作符分别给出了两个重载,正是考虑到有时可以直接操作于源序列,而有时不 得不先选择序列中元素的某个属性,随后再执行聚集操作
下面给出《LINQ in Action》中按照操作类型分组的标准查询操作符
The standard query operators grouped in families
作者:Lucifer Xue
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。