集合在C#3.0中通过称为语言集成查询(Language Integrated Query, LINQ)的一套编程API进行了大刀阔斧的改革。通过一系列扩展方法和Lambda表达式,LINQ提供了一套功能超凡的API来操纵集合。本章重点是标准查询操作符,它通过直接调用扩展方法来发挥Linq的作用。
集合初始化器(collection initializers)允许采用和数组声明相似的方式,在集合实例化期间用一组初始成员构造该集合。也和对象初始化器的语法相似,都是在构造函数调用的后面添加一对大括号,再在大括号内添加初始化列表。应用集合初始化器的集合类型应实现ICollection< T >接口确保集合包含Add()方法。
public static void Main()
{
List sevenWorldBlunders;
sevenWorldBlunders = new List()
{
// 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);
}
"运行时"根本不知foreach为何物。C#编译器会对代码进行必要的转换。
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);
}
迭代器(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找不到枚举模式的恰当实现时,编译器才会检查集合是否实现了接口。
IEnumerable上的每个方法都是标准查询操作符,用于为所操作的集合提供查询功能。
获取一个实参并返回Boolean值的委托表达式称为谓词(predicate)。从技术上说,Where()方法的结果是一个对象,它封装了根据一个给定谓词对一个给定序列进行筛选的操作。表达式传给集合,”保存“起来但不马上执行。
C#3.0通过LINQ显著增强了集合处理。其中两处增强是匿名类型和隐式局部遍历。但随着C#7.0元组语法的发布,匿名类终于也要”功成身退“了。如果不用C#7.0或更高版本,仍然可以了解一下匿名类型。
匿名类型是编译器声明的数据类型,而不是显示的类定义来声明。
var patent1 =
new
{
Title = "Bifocals",
YearOfPublication = "1784"
};
var patent2 =
new
{
Title = "Phonograph",
YearOfPublication = "1877"
};
var patent3 =
new
{
patent1.Title,
// Renamed to show property naming
Year = patent1.YearOfPublication
};
匿名类型存粹是一项C#语言功能,不是"运行时"中的新类型。编译器遇到匿名类型时,会自动生成CIL代码,其属性对应于在匿名类型声明中声明的值和数据类型。
IEnumerable fileList = Directory.EnumerateFiles(
rootDirectory, searchPattern);
var items = fileList.Select(
file =>
{
FileInfo fileInfo = new(file);
return new
{
FileName = fileInfo.Name,
Size = fileInfo.Length
};
});