C#8.0本质论第十五章–支持标准查询操作的集合接口
集合在C#3.0中通过称为语言集成查询(Language Integrated Query, LINQ)的一套编程API进行了大刀阔斧的改革。通过一系列扩展方法和Lambda表达式,LINQ提供了一套功能超凡的API来操纵集合。本章重点是标准查询操作符,它通过直接调用扩展方法来发挥Linq的作用。
15.1集合初始化器
集合初始化器(collection initializers)允许采用和数组声明相似的方式,在集合实例化期间用一组初始成员构造该集合。也和对象初始化器的语法相似,都是在构造函数调用的后面添加一对大括号,再在大括号内添加初始化列表。应用集合初始化器的集合类型应实现ICollection< T >接口确保集合包含Add()方法。
public static void Main()
{List<string> sevenWorldBlunders;sevenWorldBlunders = new List<string>(){// Quotes from Gandhi"Wealth without work","Pleasure without conscience","Knowledge without character","Commerce without morality","Science without humanity","Worship without sacrifice","Politics without principle"};Print(sevenWorldBlunders);
}
15.2IEnumerable使类成为集合
"运行时"根本不知foreach为何物。C#编译器会对代码进行必要的转换。
15.2.1foreach之于数组
int[] array = new int[] { 1, 2, 3, 4, 5, 6 };
foreach(int item in array)
{Console.WriteLine(item);
}
C#编译器在生成CIL时,为这段代码创建了一个等价的for循环
int[] tempArray;
int[] array = new int[] { 1, 2, 3, 4, 5, 6 };tempArray = array;
for(int counter = 0; (counter < tempArray.Length); counter++)
{int item = tempArray[counter];Console.WriteLine(item);
}
15.2.2基于IEnumerable遍历集合
迭代器(iterator)模式应运而生。只要能确定第一个和下一个元素,就不需要事先知道元素总数,也不需要按索引获取元素。
while(stack.MoveNext())
{ number = stack.Current();Console.WriteLine(number);
}
上面代码的问题在于,如同时又两个循环交错遍历同一个集合,则集合必须维持当前元素的一个状态指示器,交错的循环可能相互干扰。为解决该问题,集合类不直接支持IEnumerator接口,而是支持IEnumerable接口,唯一的方法就是GetEnumerator()。不是由集合类来维持状态,相反,是由一个不同的类来支持IEnumerator接口,并负责维护循环遍历的状态。
C#编译器不要求一定要实现IEnumerable才能对一个数据类型进行遍历。相反,编译器采用称为"Duck typing"的概念,也就是查找会返回“包含Current属性和MoveNext方法的一个类型“的GetEnumerator()方法。Duck typing按名称查找方法,而不依赖接口或显示方法调用。当Duck typing找不到枚举模式的恰当实现时,编译器才会检查集合是否实现了接口。
15.2.3foerach循环内不要修改
15.3标准查询操作符
IEnumerable上的每个方法都是标准查询操作符,用于为所操作的集合提供查询功能。
15.3.1使用Where()来赛选
获取一个实参并返回Boolean值的委托表达式称为谓词(predicate)。从技术上说,Where()方法的结果是一个对象,它封装了根据一个给定谓词对一个给定序列进行筛选的操作。表达式传给集合,”保存“起来但不马上执行。
15.3.2使用Select()来投射
15.3.3使用Count()对元素进行计数
15.3.4推迟执行
15.3.5使用OrderBy()和ThenBy来排序
15.3.6使用Join()执行内部联结
15.3.7使用GroupJoin实现"一对多"关系
15.3.8调用SelectMany()
15.3.9更多标准查询操作符
15.4匿名类型之于LINQ
C#3.0通过LINQ显著增强了集合处理。其中两处增强是匿名类型和隐式局部遍历。但随着C#7.0元组语法的发布,匿名类终于也要”功成身退“了。如果不用C#7.0或更高版本,仍然可以了解一下匿名类型。
15.4.1匿名类型
匿名类型是编译器声明的数据类型,而不是显示的类定义来声明。
var patent1 =new{Title = "Bifocals",YearOfPublication = "1784"};var patent2 =new{Title = "Phonograph",YearOfPublication = "1877"};var patent3 =new{patent1.Title,// Renamed to show property namingYear = patent1.YearOfPublication};
匿名类型存粹是一项C#语言功能,不是"运行时"中的新类型。编译器遇到匿名类型时,会自动生成CIL代码,其属性对应于在匿名类型声明中声明的值和数据类型。
15.4.2用LINQ投射成匿名类型
IEnumerable<string> fileList = Directory.EnumerateFiles(rootDirectory, searchPattern);
var items = fileList.Select(file =>{FileInfo fileInfo = new(file);return new{FileName = fileInfo.Name,Size = fileInfo.Length};});